v = params.get(name);
+ if (v != null && v.size() > 0) {
+ return (String[]) v.toArray(new String[v.size()]);
+ }
+
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getErrors()
+ */
+ public List getErrors() {
+ return errors;
+ }
+
+ /**
+ * Returns the canonical name of the given file.
+ *
+ * @param filename the given file
+ * @return the canonical name of the given file
+ */
+ private String getCanonicalName(String filename) {
+ int forwardSlash = filename.lastIndexOf("/");
+ int backwardSlash = filename.lastIndexOf("\\");
+ if (forwardSlash != -1 && forwardSlash > backwardSlash) {
+ filename = filename.substring(forwardSlash + 1, filename.length());
+ } else if (backwardSlash != -1 && backwardSlash >= forwardSlash) {
+ filename = filename.substring(backwardSlash + 1, filename.length());
+ }
+
+ return filename;
+ }
+
+ /**
+ * Creates a RequestContext needed by Jakarta Commons Upload.
+ *
+ * @param req the request.
+ * @return a new request context.
+ */
+ private RequestContext createRequestContext(final HttpServletRequest req) {
+ return new RequestContext() {
+ public String getCharacterEncoding() {
+ return req.getCharacterEncoding();
+ }
+
+ public String getContentType() {
+ return req.getContentType();
+ }
+
+ public int getContentLength() {
+ return req.getContentLength();
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return req.getInputStream();
+ }
+ };
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/dispatcher/multipart/MultiPartRequest.java b/trunk/core/src/main/java/org/apache/struts2/dispatcher/multipart/MultiPartRequest.java
new file mode 100644
index 000000000..35fc41da0
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/dispatcher/multipart/MultiPartRequest.java
@@ -0,0 +1,128 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.dispatcher.multipart;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Abstract wrapper class HTTP requests to handle multi-part data.
+ *
+ */
+public abstract class MultiPartRequest {
+
+ protected static Log log = LogFactory.getLog(MultiPartRequest.class);
+
+
+ /**
+ * Returns true if the request is multipart form data, false otherwise.
+ *
+ * @param request the http servlet request.
+ * @return true if the request is multipart form data, false otherwise.
+ */
+ public static boolean isMultiPart(HttpServletRequest request) {
+ String content_type = request.getContentType();
+ return content_type != null && content_type.indexOf("multipart/form-data") != -1;
+ }
+
+ /**
+ * Returns an enumeration of the parameter names for uploaded files
+ *
+ * @return an enumeration of the parameter names for uploaded files
+ */
+ public abstract Enumeration getFileParameterNames();
+
+ /**
+ * Returns the content type(s) of the file(s) associated with the specified field name
+ * (as supplied by the client browser), or null if no files are associated with the
+ * given field name.
+ *
+ * @param fieldName input field name
+ * @return an array of content encoding for the specified input field name or null if
+ * no content type was specified.
+ */
+ public abstract String[] getContentType(String fieldName);
+
+ /**
+ * Returns a {@link java.io.File} object for the filename specified or null if no files
+ * are associated with the given field name.
+ *
+ * @param fieldName input field name
+ * @return a File[] object for files associated with the specified input field name
+ */
+ public abstract File[] getFile(String fieldName);
+
+ /**
+ * Returns a String[] of file names for files associated with the specified input field name
+ *
+ * @param fieldName input field name
+ * @return a String[] of file names for files associated with the specified input field name
+ */
+ public abstract String[] getFileNames(String fieldName);
+
+ /**
+ * Returns the file system name(s) of files associated with the given field name or
+ * null if no files are associated with the given field name.
+ *
+ * @param fieldName input field name
+ * @return the file system name(s) of files associated with the given field name
+ */
+ public abstract String[] getFilesystemName(String fieldName);
+
+ /**
+ * Returns the specified request parameter.
+ *
+ * @param name the name of the parameter to get
+ * @return the parameter or null if it was not found.
+ */
+ public abstract String getParameter(String name);
+
+ /**
+ * Returns an enumeration of String parameter names.
+ *
+ * @return an enumeration of String parameter names.
+ */
+ public abstract Enumeration getParameterNames();
+
+ /**
+ * Returns a list of all parameter values associated with a parameter name. If there is only
+ * one parameter value per name the resulting array will be of length 1.
+ *
+ * @param name the name of the parameter.
+ * @return an array of all values associated with the parameter name.
+ */
+ public abstract String[] getParameterValues(String name);
+
+ /**
+ * Returns a list of error messages that may have occurred while processing the request.
+ * If there are no errors, an empty list is returned. If the underlying implementation
+ * (ie: pell, cos, jakarta, etc) cannot support providing these errors, an empty list is
+ * also returned. This list of errors is repoted back to the
+ * {@link MultiPartRequestWrapper}'s errors field.
+ *
+ * @return a list of Strings that represent various errors during parsing
+ */
+ public abstract List getErrors();
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/dispatcher/multipart/MultiPartRequestWrapper.java b/trunk/core/src/main/java/org/apache/struts2/dispatcher/multipart/MultiPartRequestWrapper.java
new file mode 100644
index 000000000..3b754cb51
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/dispatcher/multipart/MultiPartRequestWrapper.java
@@ -0,0 +1,305 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.dispatcher.multipart;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.config.Settings;
+import org.apache.struts2.dispatcher.StrutsRequestWrapper;
+import org.apache.struts2.util.ClassLoaderUtils;
+
+
+/**
+ * Parses a multipart request and provides a wrapper around the request. The parsing implementation used
+ * depends on the struts.multipart.parser setting. It should be set to a class which
+ * extends {@link org.apache.struts2.dispatcher.multipart.MultiPartRequest}.
+ *
+ * Struts ships with three implementations,
+ * {@link org.apache.struts2.dispatcher.multipart.PellMultiPartRequest}, and
+ * {@link org.apache.struts2.dispatcher.multipart.CosMultiPartRequest} and
+ * {@link org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest}. The Jakarta implementation
+ * is the default. The struts.multipart.parser property should be set to jakarta for the
+ * Jakarta implementation, pell for the Pell implementation and cos for the Jason Hunter
+ * implementation.
+ *
+ * The files are uploaded when the object is instantiated. If there are any errors they are logged using
+ * {@link #addError(String)}. An action handling a multipart form should first check {@link #hasErrors()}
+ * before doing any other processing.
+ *
+ */
+public class MultiPartRequestWrapper extends StrutsRequestWrapper {
+ protected static final Log log = LogFactory.getLog(MultiPartRequestWrapper.class);
+
+ Collection errors;
+ MultiPartRequest multi;
+
+ /**
+ * Instantiates the appropriate MultiPartRequest parser implementation and processes the data.
+ *
+ * @param request the servlet request object
+ * @param saveDir directory to save the file(s) to
+ * @param maxSize maximum file size allowed
+ */
+ public MultiPartRequestWrapper(HttpServletRequest request, String saveDir, int maxSize) {
+ super(request);
+
+ if (request instanceof MultiPartRequest) {
+ multi = (MultiPartRequest) request;
+ } else {
+ String parser = Settings.get(StrutsConstants.STRUTS_MULTIPART_PARSER);
+
+ // If it's not set, use Jakarta
+ if (parser.equals("")) {
+ log.warn("Property struts.multipart.parser not set." +
+ " Using org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest");
+ parser = "org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest";
+ }
+ // legacy support for old style property values
+ else if (parser.equals("pell")) {
+ parser = "org.apache.struts2.dispatcher.multipart.PellMultiPartRequest";
+ } else if (parser.equals("cos")) {
+ parser = "org.apache.struts2.dispatcher.multipart.CosMultiPartRequest";
+ } else if (parser.equals("jakarta")) {
+ parser = "org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest";
+ }
+
+ try {
+ Class baseClazz = org.apache.struts2.dispatcher.multipart.MultiPartRequest.class;
+
+ Class clazz = ClassLoaderUtils.loadClass(parser, MultiPartRequestWrapper.class);
+
+ // make sure it extends MultiPartRequest
+ if (!baseClazz.isAssignableFrom(clazz)) {
+ addError("Class '" + parser + "' does not extend MultiPartRequest");
+
+ return;
+ }
+
+ // get the constructor
+ Constructor ctor = clazz.getDeclaredConstructor(new Class[]{
+ ClassLoaderUtils.loadClass("javax.servlet.http.HttpServletRequest", MultiPartRequestWrapper.class),
+ java.lang.String.class, int.class
+ });
+
+ // build the parameter list
+ Object[] parms = new Object[]{
+ request, saveDir, new Integer(maxSize)
+ };
+
+ // instantiate it
+ multi = (MultiPartRequest) ctor.newInstance(parms);
+ for (Iterator iter = multi.getErrors().iterator(); iter.hasNext();) {
+ String error = (String) iter.next();
+ addError(error);
+ }
+ } catch (ClassNotFoundException e) {
+ addError("Class: " + parser + " not found.");
+ } catch (NoSuchMethodException e) {
+ addError("Constructor error for " + parser + ": " + e);
+ } catch (InstantiationException e) {
+ addError("Error instantiating " + parser + ": " + e);
+ } catch (IllegalAccessException e) {
+ addError("Access errror for " + parser + ": " + e);
+ } catch (InvocationTargetException e) {
+ // This is a wrapper for any exceptions thrown by the constructor called from newInstance
+ addError(e.getTargetException().toString());
+ }
+ }
+ }
+
+ /**
+ * Get an enumeration of the parameter names for uploaded files
+ *
+ * @return enumeration of parameter names for uploaded files
+ */
+ public Enumeration getFileParameterNames() {
+ if (multi == null) {
+ return null;
+ }
+
+ return multi.getFileParameterNames();
+ }
+
+ /**
+ * Get an array of content encoding for the specified input field name or null if
+ * no content type was specified.
+ *
+ * @param name input field name
+ * @return an array of content encoding for the specified input field name
+ */
+ public String[] getContentTypes(String name) {
+ if (multi == null) {
+ return null;
+ }
+
+ return multi.getContentType(name);
+ }
+
+ /**
+ * Get a {@link java.io.File[]} for the given input field name.
+ *
+ * @param fieldName input field name
+ * @return a File[] object for files associated with the specified input field name
+ */
+ public File[] getFiles(String fieldName) {
+ if (multi == null) {
+ return null;
+ }
+
+ return multi.getFile(fieldName);
+ }
+
+ /**
+ * Get a String array of the file names for uploaded files
+ *
+ * @return a String[] of file names for uploaded files
+ */
+ public String[] getFileNames(String fieldName) {
+ if (multi == null) {
+ return null;
+ }
+
+ return multi.getFileNames(fieldName);
+ }
+
+ /**
+ * Get the filename(s) of the file(s) uploaded for the given input field name.
+ * Returns null if the file is not found.
+ *
+ * @param fieldName input field name
+ * @return the filename(s) of the file(s) uploaded for the given input field name or
+ * null if name not found.
+ */
+ public String[] getFileSystemNames(String fieldName) {
+ if (multi == null) {
+ return null;
+ }
+
+ return multi.getFilesystemName(fieldName);
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#getParameter(String)
+ */
+ public String getParameter(String name) {
+ return ((multi == null) || (multi.getParameter(name) == null)) ? super.getParameter(name) : multi.getParameter(name);
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#getParameterMap()
+ */
+ public Map getParameterMap() {
+ Map map = new HashMap();
+ Enumeration enumeration = getParameterNames();
+
+ while (enumeration.hasMoreElements()) {
+ String name = (String) enumeration.nextElement();
+ map.put(name, this.getParameterValues(name));
+ }
+
+ return map;
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#getParameterNames()
+ */
+ public Enumeration getParameterNames() {
+ if (multi == null) {
+ return super.getParameterNames();
+ } else {
+ return mergeParams(multi.getParameterNames(), super.getParameterNames());
+ }
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#getParameterValues(String)
+ */
+ public String[] getParameterValues(String name) {
+ return ((multi == null) || (multi.getParameterValues(name) == null)) ? super.getParameterValues(name) : multi.getParameterValues(name);
+ }
+
+ /**
+ * Returns true if any errors occured when parsing the HTTP multipart request, false otherwise.
+ *
+ * @return true if any errors occured when parsing the HTTP multipart request, false otherwise.
+ */
+ public boolean hasErrors() {
+ if ((errors == null) || errors.isEmpty()) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Returns a collection of any errors generated when parsing the multipart request.
+ *
+ * @return the error Collection.
+ */
+ public Collection getErrors() {
+ return errors;
+ }
+
+ /**
+ * Adds an error message.
+ *
+ * @param anErrorMessage the error message to report.
+ */
+ protected void addError(String anErrorMessage) {
+ if (errors == null) {
+ errors = new ArrayList();
+ }
+
+ errors.add(anErrorMessage);
+ }
+
+ /**
+ * Merges 2 enumeration of parameters as one.
+ *
+ * @param params1 the first enumeration.
+ * @param params2 the second enumeration.
+ * @return a single Enumeration of all elements from both Enumerations.
+ */
+ protected Enumeration mergeParams(Enumeration params1, Enumeration params2) {
+ Vector temp = new Vector();
+
+ while (params1.hasMoreElements()) {
+ temp.add(params1.nextElement());
+ }
+
+ while (params2.hasMoreElements()) {
+ temp.add(params2.nextElement());
+ }
+
+ return temp.elements();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/dispatcher/multipart/package.html b/trunk/core/src/main/java/org/apache/struts2/dispatcher/multipart/package.html
new file mode 100644
index 000000000..6f5f810d1
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/dispatcher/multipart/package.html
@@ -0,0 +1 @@
+Classes to help dispatch multipart HTTP requests.
diff --git a/trunk/core/src/main/java/org/apache/struts2/dispatcher/package.html b/trunk/core/src/main/java/org/apache/struts2/dispatcher/package.html
new file mode 100644
index 000000000..588e5d0d4
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/dispatcher/package.html
@@ -0,0 +1 @@
+Classes for action dispatching in Struts (the Controller part of MVC).
diff --git a/trunk/core/src/main/java/org/apache/struts2/impl/ActionContextImpl.java b/trunk/core/src/main/java/org/apache/struts2/impl/ActionContextImpl.java
new file mode 100644
index 000000000..bc61e0414
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/impl/ActionContextImpl.java
@@ -0,0 +1,64 @@
+// Copyright 2006 Google Inc. All Rights Reserved.
+
+package org.apache.struts2.impl;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.struts2.spi.ActionContext;
+import org.apache.struts2.spi.Result;
+
+import com.opensymphony.xwork2.ActionInvocation;
+
+public class ActionContextImpl implements ActionContext {
+
+ final ActionInvocation invocation;
+
+ public ActionContextImpl(ActionInvocation invocation) {
+ this.invocation = invocation;
+ }
+
+ public Object getAction() {
+ return invocation.getAction();
+ }
+
+ public Method getMethod() {
+ String methodName = invocation.getProxy().getMethod();
+ try {
+ return getAction().getClass().getMethod(methodName);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String getActionName() {
+ return invocation.getProxy().getActionName();
+ }
+
+ public String getNamespacePath() {
+ return invocation.getProxy().getNamespace();
+ }
+
+ // TODO: Do something with these.
+ List resultInterceptors = new ArrayList();
+
+ public void addResultInterceptor(Result interceptor) {
+ resultInterceptors.add(interceptor);
+ }
+
+ public Result getResult() {
+ // TODO
+ throw new UnsupportedOperationException();
+ }
+
+ public ActionContext getPrevious() {
+ // TODO
+ throw new UnsupportedOperationException();
+ }
+
+ public ActionContext getNext() {
+ // TODO
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/impl/InterceptorAdapter.java b/trunk/core/src/main/java/org/apache/struts2/impl/InterceptorAdapter.java
new file mode 100644
index 000000000..68e3d57ec
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/impl/InterceptorAdapter.java
@@ -0,0 +1,49 @@
+// Copyright 2006 Google Inc. All Rights Reserved.
+
+package org.apache.struts2.impl;
+
+import static org.apache.struts2.impl.RequestContextImpl.ILLEGAL_PROCEED;
+
+import java.util.concurrent.Callable;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.Interceptor;
+
+public class InterceptorAdapter implements Interceptor {
+
+ private static final long serialVersionUID = 8020658947818231684L;
+ final org.apache.struts2.spi.Interceptor delegate;
+
+ public InterceptorAdapter(org.apache.struts2.spi.Interceptor delegate) {
+ this.delegate = delegate;
+ }
+
+ public String intercept(final ActionInvocation invocation) throws Exception {
+ final RequestContextImpl requestContext = RequestContextImpl.get();
+
+ // Save the existing proceed implementation so we can restore it later.
+ Callable previous = requestContext.getProceed();
+
+ requestContext.setProceed(new Callable() {
+ public String call() throws Exception {
+ // This proceed implementation is no longer valid past this point.
+ requestContext.setProceed(ILLEGAL_PROCEED);
+ try {
+ return invocation.invoke();
+ } finally {
+ // We're valid again.
+ requestContext.setProceed(this);
+ }
+ }
+ });
+
+ try {
+ return delegate.intercept(requestContext);
+ } finally {
+ requestContext.setProceed(previous);
+ }
+ }
+
+ public void destroy() {}
+ public void init() {}
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/impl/MessagesImpl.java b/trunk/core/src/main/java/org/apache/struts2/impl/MessagesImpl.java
new file mode 100644
index 000000000..32a759e37
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/impl/MessagesImpl.java
@@ -0,0 +1,134 @@
+// Copyright 2006 Google Inc. All Rights Reserved.
+
+package org.apache.struts2.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.struts2.Messages;
+
+import com.opensymphony.xwork2.DefaultTextProvider;
+import com.opensymphony.xwork2.TextProvider;
+
+public class MessagesImpl implements Messages {
+
+ final TextProvider textProvider = DefaultTextProvider.INSTANCE;
+ Map fieldMap = new HashMap();
+ Map> severityMap = new EnumMap>(Severity.class);
+
+ public Messages forField(String fieldName) {
+ Messages forField = fieldMap.get(fieldName);
+ if (forField == null) {
+ forField = new MessagesImpl();
+ fieldMap.put(fieldName, forField);
+ }
+ return forField;
+ }
+
+ public Map forFields() {
+ return fieldMap;
+ }
+
+ public void addInformation(String key) {
+ forSeverity(Severity.INFO).add(textProvider.getText(key));
+ }
+
+ public void addInformation(String key, String... arguments) {
+ forSeverity(Severity.INFO).add(textProvider.getText(key, arguments));
+ }
+
+ public void addWarning(String key) {
+ forSeverity(Severity.WARN).add(textProvider.getText(key));
+ }
+
+ public void addWarning(String key, String... arguments) {
+ forSeverity(Severity.WARN).add(textProvider.getText(key, arguments));
+ }
+
+ public void addError(String key) {
+ forSeverity(Severity.ERROR).add(textProvider.getText(key));
+ }
+
+ public void addError(String key, String... arguments) {
+ forSeverity(Severity.ERROR).add(textProvider.getText(key, arguments));
+ }
+
+ public void add(Severity severity, String key) {
+ forSeverity(severity).add(textProvider.getText(key));
+ }
+
+ public void add(Severity severity, String key, String... arguments) {
+ forSeverity(severity).add(textProvider.getText(key, arguments));
+ }
+
+ public Set getSeverities() {
+ Set severities = EnumSet.noneOf(Severity.class);
+ for (Severity severity : Severity.values()) {
+ List messages = severityMap.get(severity);
+ if (messages != null && !messages.isEmpty()) {
+ severities.add(severity);
+ }
+ }
+ return Collections.unmodifiableSet(severities);
+ }
+
+ public List forSeverity(Severity severity) {
+ List messages = severityMap.get(severity);
+ if (messages == null) {
+ messages = new ArrayList();
+ severityMap.put(severity, messages);
+ }
+ return messages;
+ }
+
+ public List getErrors() {
+ return forSeverity(Severity.ERROR);
+ }
+
+ public List getWarnings() {
+ return forSeverity(Severity.WARN);
+ }
+
+ public List getInformation() {
+ return forSeverity(Severity.INFO);
+ }
+
+ public boolean hasErrors() {
+ return !isEmpty(Severity.ERROR);
+ }
+
+ public boolean hasWarnings() {
+ return !isEmpty(Severity.WARN);
+ }
+
+ public boolean hasInformation() {
+ return !isEmpty(Severity.INFO);
+ }
+
+ public boolean isEmpty() {
+ for (Severity severity : Severity.values())
+ if (!isEmpty(severity))
+ return false;
+
+ return true;
+ }
+
+ public boolean isEmpty(Severity severity) {
+ List messages = severityMap.get(severity);
+ if (messages != null && !messages.isEmpty()) {
+ return false;
+ }
+
+ for (Messages fieldMessages : fieldMap.values())
+ if (!fieldMessages.isEmpty(severity))
+ return false;
+
+ return true;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/impl/RequestContextImpl.java b/trunk/core/src/main/java/org/apache/struts2/impl/RequestContextImpl.java
new file mode 100644
index 000000000..1d853806e
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/impl/RequestContextImpl.java
@@ -0,0 +1,177 @@
+// Copyright 2006 Google Inc. All Rights Reserved.
+
+package org.apache.struts2.impl;
+
+import static org.apache.struts2.StrutsStatics.HTTP_REQUEST;
+import static org.apache.struts2.StrutsStatics.HTTP_RESPONSE;
+import static org.apache.struts2.StrutsStatics.SERVLET_CONTEXT;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.Messages;
+import org.apache.struts2.dispatcher.RequestMap;
+import org.apache.struts2.spi.ActionContext;
+import org.apache.struts2.spi.RequestContext;
+import org.apache.struts2.spi.ValueStack;
+
+import com.opensymphony.xwork2.ActionInvocation;
+
+public class RequestContextImpl implements RequestContext {
+
+ com.opensymphony.xwork2.ActionContext xworkContext;
+ ActionContext actionContext;
+ Messages messages = new MessagesImpl();
+
+ public static final Callable ILLEGAL_PROCEED = new Callable() {
+ public String call() throws Exception {
+ throw new IllegalStateException();
+ }
+ };
+
+ public RequestContextImpl(com.opensymphony.xwork2.ActionContext xworkContext) {
+ this.xworkContext = xworkContext;
+ }
+
+ public ActionContext getActionContext() {
+ return actionContext;
+ }
+
+ public Object getAction() {
+ return getActionContext().getAction();
+ }
+
+ void setActionContext(ActionContext actionContext) {
+ this.actionContext = actionContext;
+ }
+
+ public Map getParameterMap() {
+ return xworkContext.getParameters();
+ }
+
+ Map attributeMap;
+
+ public Map getAttributeMap() {
+ if (attributeMap == null) {
+ attributeMap = new RequestMap(getServletRequest());
+ }
+ return attributeMap;
+ }
+
+ public Map getSessionMap() {
+ return xworkContext.getSession();
+ }
+
+ public Map getApplicationMap() {
+ return xworkContext.getApplication();
+ }
+
+ public List findCookiesForName(String name) {
+ List cookies = new ArrayList();
+ for (Cookie cookie : getServletRequest().getCookies())
+ if (name.equals(cookie.getName()))
+ cookies.add(cookie);
+
+ return cookies;
+ }
+
+ public Locale getLocale() {
+ return xworkContext.getLocale();
+ }
+
+ public void setLocale(Locale locale) {
+ xworkContext.setLocale(locale);
+ }
+
+ public Messages getMessages() {
+ return messages;
+ }
+
+ public HttpServletRequest getServletRequest() {
+ return (HttpServletRequest) xworkContext.get(HTTP_REQUEST);
+ }
+
+ public HttpServletResponse getServletResponse() {
+ return (HttpServletResponse) xworkContext.get(HTTP_RESPONSE);
+ }
+
+ public ServletContext getServletContext() {
+ return (ServletContext) xworkContext.get(SERVLET_CONTEXT);
+ }
+
+ ValueStack valueStack;
+
+ public ValueStack getValueStack() {
+ if (valueStack == null) {
+ valueStack = new ValueStackAdapter(xworkContext.getValueStack());
+ }
+ return valueStack;
+ }
+
+ Callable proceed = ILLEGAL_PROCEED;
+
+ public String proceed() throws Exception {
+ return proceed.call();
+ }
+
+ public void setProceed(Callable proceed) {
+ this.proceed = proceed;
+ }
+
+ public Callable getProceed() {
+ return proceed;
+ }
+
+ static ThreadLocal threadLocalRequestContext = new ThreadLocal() {
+ protected RequestContextImpl[] initialValue() {
+ return new RequestContextImpl[1];
+ }
+ };
+
+ /**
+ * Creates RequestContext if necessary. Always creates a new ActionContext and restores an existing ActionContext
+ * when finished.
+ */
+ public static String callInContext(ActionInvocation invocation, Callable callable)
+ throws Exception {
+ RequestContextImpl[] reference = threadLocalRequestContext.get();
+
+ if (reference[0] == null) {
+ // Initial invocation.
+ reference[0] = new RequestContextImpl(invocation.getInvocationContext());
+ reference[0].setActionContext(new ActionContextImpl(invocation));
+ try {
+ return callable.call();
+ } finally {
+ reference[0] = null;
+ }
+ } else {
+ // Nested invocation.
+ RequestContextImpl requestContext = reference[0];
+ ActionContext previous = requestContext.getActionContext();
+ requestContext.setActionContext(new ActionContextImpl(invocation));
+ try {
+ return callable.call();
+ } finally {
+ requestContext.setActionContext(previous);
+ }
+ }
+ }
+
+ public static RequestContextImpl get() {
+ RequestContextImpl requestContext = threadLocalRequestContext.get()[0];
+
+ if (requestContext == null)
+ throw new IllegalStateException("RequestContext has not been created.");
+
+ return requestContext;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/impl/ResultAdapter.java b/trunk/core/src/main/java/org/apache/struts2/impl/ResultAdapter.java
new file mode 100644
index 000000000..f71c8f32f
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/impl/ResultAdapter.java
@@ -0,0 +1,20 @@
+// Copyright 2006 Google Inc. All Rights Reserved.
+
+package org.apache.struts2.impl;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.Result;
+
+public class ResultAdapter implements Result {
+
+ private static final long serialVersionUID = -5107033078266553554L;
+ final org.apache.struts2.spi.Result delegate;
+
+ public ResultAdapter(org.apache.struts2.spi.Result delegate) {
+ this.delegate = delegate;
+ }
+
+ public void execute(ActionInvocation invocation) throws Exception {
+ delegate.execute(RequestContextImpl.get());
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/impl/StrutsActionProxy.java b/trunk/core/src/main/java/org/apache/struts2/impl/StrutsActionProxy.java
new file mode 100644
index 000000000..f13340531
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/impl/StrutsActionProxy.java
@@ -0,0 +1,35 @@
+// Copyright 2006 Google Inc. All Rights Reserved.
+
+package org.apache.struts2.impl;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.DefaultActionProxy;
+import com.opensymphony.xwork2.config.Configuration;
+
+public class StrutsActionProxy extends DefaultActionProxy {
+
+ private static final long serialVersionUID = -2434901249671934080L;
+
+ public StrutsActionProxy(Configuration cfg, String namespace, String actionName, Map extraContext,
+ boolean executeResult, boolean cleanupContext) throws Exception {
+ super(cfg, namespace, actionName, extraContext, executeResult, cleanupContext);
+ }
+
+ public String execute() throws Exception {
+ ActionContext previous = ActionContext.getContext();
+ ActionContext.setContext(invocation.getInvocationContext());
+ try {
+ return RequestContextImpl.callInContext(invocation, new Callable() {
+ public String call() throws Exception {
+ return invocation.invoke();
+ }
+ });
+ } finally {
+ if (cleanupContext)
+ ActionContext.setContext(previous);
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/impl/StrutsActionProxyFactory.java b/trunk/core/src/main/java/org/apache/struts2/impl/StrutsActionProxyFactory.java
new file mode 100644
index 000000000..c55e5fd14
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/impl/StrutsActionProxyFactory.java
@@ -0,0 +1,22 @@
+// Copyright 2006 Google Inc. All Rights Reserved.
+
+package org.apache.struts2.impl;
+
+import java.util.Map;
+
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.DefaultActionProxyFactory;
+import com.opensymphony.xwork2.config.Configuration;
+
+public class StrutsActionProxyFactory extends DefaultActionProxyFactory {
+
+ public ActionProxy createActionProxy(Configuration config, String namespace, String actionName, Map extraContext)
+ throws Exception {
+ return new StrutsActionProxy(config, namespace, actionName, extraContext, true, true);
+ }
+
+ public ActionProxy createActionProxy(Configuration config, String namespace, String actionName, Map extraContext,
+ boolean executeResult, boolean cleanupContext) throws Exception {
+ return new StrutsActionProxy(config, namespace, actionName, extraContext, executeResult, cleanupContext);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/impl/StrutsObjectFactory.java b/trunk/core/src/main/java/org/apache/struts2/impl/StrutsObjectFactory.java
new file mode 100644
index 000000000..dda52dfd6
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/impl/StrutsObjectFactory.java
@@ -0,0 +1,84 @@
+// Copyright 2006 Google Inc. All Rights Reserved.
+
+package org.apache.struts2.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.Result;
+import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.config.entities.InterceptorConfig;
+import com.opensymphony.xwork2.config.entities.ResultConfig;
+import com.opensymphony.xwork2.interceptor.Interceptor;
+import com.opensymphony.xwork2.util.OgnlUtil;
+
+public class StrutsObjectFactory extends ObjectFactory {
+
+ public Interceptor buildInterceptor(InterceptorConfig interceptorConfig, Map refParams)
+ throws ConfigurationException {
+ String className = interceptorConfig.getClassName();
+
+ Map params = new HashMap();
+ Map typeParams = interceptorConfig.getParams();
+ if (typeParams != null && !typeParams.isEmpty())
+ params.putAll(typeParams);
+ if (refParams != null && !refParams.isEmpty())
+ params.putAll(refParams);
+ params.putAll(refParams);
+
+ try {
+ // interceptor instances are long-lived and used across user sessions, so don't try to pass in any extra
+ // context
+ Object o = buildBean(className, null);
+ OgnlUtil.setProperties(params, o);
+
+ if (o instanceof Interceptor) {
+ Interceptor interceptor = (Interceptor) o;
+ interceptor.init();
+ return interceptor;
+ }
+
+ if (o instanceof org.apache.struts2.spi.Interceptor)
+ return new InterceptorAdapter((org.apache.struts2.spi.Interceptor) o);
+
+ throw new ConfigurationException(
+ "Class [" + className + "] does not implement Interceptor", interceptorConfig);
+ } catch (InstantiationException e) {
+ throw new ConfigurationException(
+ "Unable to instantiate an instance of Interceptor class [" + className + "].",
+ e, interceptorConfig);
+ } catch (IllegalAccessException e) {
+ throw new ConfigurationException(
+ "IllegalAccessException while attempting to instantiate an instance of Interceptor class ["
+ + className + "].",
+ e, interceptorConfig);
+ } catch (Exception e) {
+ throw new ConfigurationException(
+ "Caught Exception while registering Interceptor class " + className,
+ e, interceptorConfig);
+ } catch (NoClassDefFoundError e) {
+ throw new ConfigurationException(
+ "Could not load class " + className
+ + ". Perhaps it exists but certain dependencies are not available?",
+ e, interceptorConfig);
+ }
+ }
+
+ public Result buildResult(ResultConfig resultConfig, Map extraContext) throws Exception {
+ String resultClassName = resultConfig.getClassName();
+ if (resultClassName == null)
+ return null;
+
+ Object result = buildBean(resultClassName, extraContext);
+ OgnlUtil.setProperties(resultConfig.getParams(), result, extraContext);
+
+ if (result instanceof Result)
+ return (Result) result;
+
+ if (result instanceof org.apache.struts2.spi.Result)
+ return new ResultAdapter((org.apache.struts2.spi.Result) result);
+
+ throw new ConfigurationException(result.getClass().getName() + " does not implement Result.");
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/impl/ValueStackAdapter.java b/trunk/core/src/main/java/org/apache/struts2/impl/ValueStackAdapter.java
new file mode 100644
index 000000000..652a4fcdd
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/impl/ValueStackAdapter.java
@@ -0,0 +1,58 @@
+// Copyright 2006 Google Inc. All Rights Reserved.
+
+package org.apache.struts2.impl;
+
+import java.util.Iterator;
+
+import org.apache.struts2.spi.ValueStack;
+
+import com.opensymphony.xwork2.util.ValueStackFactory;
+
+public class ValueStackAdapter implements ValueStack {
+
+ final com.opensymphony.xwork2.util.ValueStack delegate;
+
+ public ValueStackAdapter(com.opensymphony.xwork2.util.ValueStack delegate) {
+ this.delegate = delegate;
+ }
+
+ public Object peek() {
+ return delegate.peek();
+ }
+
+ public Object pop() {
+ return delegate.pop();
+ }
+
+ public void push(Object o) {
+ delegate.push(o);
+ }
+
+ public ValueStack clone() {
+ return new ValueStackAdapter(ValueStackFactory.getFactory().createValueStack(delegate));
+ }
+
+ public Object get(String expr) {
+ return delegate.findValue(expr);
+ }
+
+ public T get(String expr, Class requiredType) {
+ return (T) delegate.findValue(expr, requiredType);
+ }
+
+ public String getString(String expr) {
+ return delegate.findString(expr);
+ }
+
+ public void set(String expr, Object o) {
+ delegate.set(expr, o);
+ }
+
+ public int size() {
+ return delegate.size();
+ }
+
+ public Iterator iterator() {
+ return delegate.getRoot().iterator();
+ }
+}
\ No newline at end of file
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/ApplicationAware.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/ApplicationAware.java
new file mode 100644
index 000000000..7c173c1e1
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/ApplicationAware.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.util.Map;
+
+
+/**
+ * Actions that want to be aware of the application Map object should implement this interface.
+ * This will give them access to a Map where they can put objects that should be available
+ * to other parts of the application.
+ *
+ * Typical uses are configuration objects and caches.
+ *
+ */
+public interface ApplicationAware {
+
+ /**
+ * Sets the map of application properties in the implementing class.
+ *
+ * @param application a Map of application properties.
+ */
+ public void setApplication(Map application);
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/BackgroundProcess.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/BackgroundProcess.java
new file mode 100644
index 000000000..ba82b1719
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/BackgroundProcess.java
@@ -0,0 +1,133 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.io.Serializable;
+
+import com.opensymphony.xwork2.ActionInvocation;
+
+/**
+ * Background thread to be executed by the ExecuteAndWaitInterceptor.
+ *
+ */
+public class BackgroundProcess implements Serializable {
+
+ private static final long serialVersionUID = 3884464776311686443L;
+
+ protected Object action;
+ protected ActionInvocation invocation;
+ protected String result;
+ protected Exception exception;
+ protected boolean done;
+
+ /**
+ * Constructs a background process
+ *
+ * @param threadName The thread name
+ * @param invocation The action invocation
+ * @param threadPriority The thread priority
+ */
+ public BackgroundProcess(String threadName, final ActionInvocation invocation, int threadPriority) {
+ this.invocation = invocation;
+ this.action = invocation.getAction();
+ try {
+ final Thread t = new Thread(new Runnable() {
+ public void run() {
+ try {
+ beforeInvocation();
+ result = invocation.invokeActionOnly();
+ afterInvocation();
+ } catch (Exception e) {
+ exception = e;
+ }
+
+ done = true;
+ }
+ });
+ t.setName(threadName);
+ t.setPriority(threadPriority);
+ t.start();
+ } catch (Exception e) {
+ exception = e;
+ }
+ }
+
+ /**
+ * Called before the background thread determines the result code
+ * from the ActionInvocation.
+ *
+ * @throws Exception any exception thrown will be thrown, in turn, by the ExecuteAndWaitInterceptor
+ */
+ protected void beforeInvocation() throws Exception {
+ }
+
+ /**
+ * Called after the background thread determines the result code
+ * from the ActionInvocation, but before the background thread is
+ * marked as done.
+ *
+ * @throws Exception any exception thrown will be thrown, in turn, by the ExecuteAndWaitInterceptor
+ */
+ protected void afterInvocation() throws Exception {
+ }
+
+ /**
+ * Retrieves the action.
+ *
+ * @return the action.
+ */
+ public Object getAction() {
+ return action;
+ }
+
+ /**
+ * Retrieves the action invocation.
+ *
+ * @return the action invocation
+ */
+ public ActionInvocation getInvocation() {
+ return invocation;
+ }
+
+ /**
+ * Gets the result of the background process.
+ *
+ * @return the result; null if not done.
+ */
+ public String getResult() {
+ return result;
+ }
+
+ /**
+ * Gets the exception if any was thrown during the execution of the background process.
+ *
+ * @return the exception or null if no exception was thrown.
+ */
+ public Exception getException() {
+ return exception;
+ }
+
+ /**
+ * Returns the status of the background process.
+ *
+ * @return true if finished, false otherwise
+ */
+ public boolean isDone() {
+ return done;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/CheckboxInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/CheckboxInterceptor.java
new file mode 100644
index 000000000..b441dbb97
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/CheckboxInterceptor.java
@@ -0,0 +1,91 @@
+/*
+ * $Id: CheckboxListTest.java 439747 2006-09-03 09:22:46Z mrdon $
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.Interceptor;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ *
+ * Looks for a hidden identification field that specifies the original value of the checkbox.
+ * If the checkbox isn't submitted, insert it into the parameters as if it was with the value
+ * of 'false'.
+ *
+ *
+ *
+ * setUncheckedValue -
+ * The default value of an unchecked box can be overridden by setting the 'uncheckedValue' property.
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+public class CheckboxInterceptor implements Interceptor {
+
+ /** Auto-generated serialization id */
+ private static final long serialVersionUID = -586878104807229585L;
+
+ private String uncheckedValue = Boolean.FALSE.toString();
+
+ public void destroy() {
+ }
+
+ public void init() {
+ }
+
+ public String intercept(ActionInvocation ai) throws Exception {
+ Map parameters = ai.getInvocationContext().getParameters();
+ Map newParams = new HashMap();
+ Set keys = parameters.keySet();
+ for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
+ String key = iterator.next();
+
+ if (key.startsWith("__checkbox_")) {
+ String name = key.substring("__checkbox_".length());
+
+ iterator.remove();
+
+ // is this checkbox checked/submitted?
+ if (!parameters.containsKey(name)) {
+ // if not, let's be sure to default the value to false
+ newParams.put(name, uncheckedValue);
+ }
+ }
+ }
+
+ parameters.putAll(newParams);
+
+ return ai.invoke();
+ }
+
+ /**
+ * Overrides the default value for an unchecked checkbox
+ *
+ * @param uncheckedValue The uncheckedValue to set
+ */
+ public void setUncheckedValue(String uncheckedValue) {
+ this.uncheckedValue = uncheckedValue;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/CreateSessionInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/CreateSessionInterceptor.java
new file mode 100644
index 000000000..a81dcd4f2
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/CreateSessionInterceptor.java
@@ -0,0 +1,92 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.ServletActionContext;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+
+/**
+ *
+ *
+ * This interceptor creates the HttpSession.
+ *
+ * This is particular usefull when using the <@s.token> tag in freemarker templates.
+ * The tag do require that a HttpSession is already created since freemarker commits
+ * the response to the client immediately.
+ *
+ *
+ *
+ *
Interceptor parameters:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * Example:
+ *
+ *
+ *
+ *
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="create-session"/>
+ * <interceptor-ref name="defaultStack"/>
+ * <result name="input">input_with_token_tag.ftl</result>
+ * </action>
+ *
+ *
+ *
+ *
+ * @version $Date$ $Id$
+ */
+public class CreateSessionInterceptor extends AbstractInterceptor {
+
+ private static final long serialVersionUID = -4590322556118858869L;
+
+ private static final Log _log = LogFactory.getLog(CreateSessionInterceptor.class);
+
+
+ /* (non-Javadoc)
+ * @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
+ */
+ public String intercept(ActionInvocation invocation) throws Exception {
+ _log.debug("Creating HttpSession");
+ ServletActionContext.getRequest().getSession(true);
+ return invocation.invoke();
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptor.java
new file mode 100644
index 000000000..0e821df7d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptor.java
@@ -0,0 +1,333 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.config.entities.ResultConfig;
+import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
+
+
+/**
+ *
+ *
+ * The ExecuteAndWaitInterceptor is great for running long-lived actions in the background while showing the user a nice
+ * progress meter. This also prevents the HTTP request from timing out when the action takes more than 5 or 10 minutes.
+ *
+ *
Using this interceptor is pretty straight forward. Assuming that you are including struts-default.xml, this
+ * interceptor is already configured but is not part of any of the default stacks. Because of the nature of this
+ * interceptor, it must be the last interceptor in the stack.
+ *
+ *
This interceptor works on a per-session basis. That means that the same action name (myLongRunningAction, in the
+ * above example) cannot be run more than once at a time in a given session. On the initial request or any subsequent
+ * requests (before the action has completed), the wait result will be returned. The wait result is
+ * responsible for issuing a subsequent request back to the action, giving the effect of a self-updating progress
+ * meter .
+ *
+ *
If no "wait" result is found, Struts will automatically generate a wait result on the fly. This result is
+ * written in FreeMarker and cannot run unless FreeMarker is installed. If you don't wish to deploy with FreeMarker, you
+ * must provide your own wait result. This is generally a good thing to do anyway, as the default wait page is very
+ * plain.
+ *
+ *
Whenever the wait result is returned, the action that is currently running in the background will be placed on
+ * top of the stack . This allows you to display progress data, such as a count, in the wait page. By making the wait
+ * page automatically reload the request to the action (which will be short-circuited by the interceptor), you can give
+ * the appearance of an automatic progress meter.
+ *
+ *
This interceptor also supports using an initial wait delay. An initial delay is a time in milliseconds we let the
+ * server wait before the wait page is shown to the user. During the wait this interceptor will wake every 100 millis
+ * to check if the background process is done premature, thus if the job for some reason doesn't take to long the wait
+ * page is not shown to the user.
+ * This is useful for e.g. search actions that have a wide span of execution time. Using a delay time of 2000
+ * millis we ensure the user is presented fast search results immediately and for the slow results a wait page is used.
+ *
+ *
Important : Because the action will be running in a seperate thread, you can't use ActionContext because it
+ * is a ThreadLocal. This means if you need to access, for example, session data, you need to implement SessionAware
+ * rather than calling ActionContext.getSesion().
+ *
+ *
The thread kicked off by this interceptor will be named in the form actionName BrackgroundProcess .
+ * For example, the search action would run as a thread named searchBackgroundProcess .
+ *
+ *
+ *
+ *
Interceptor parameters:
+ *
+ *
+ *
+ *
+ *
+ * threadPriority (optional) - the priority to assign the thread. Default is Thread.NORM_PRIORITY.
+ * delay (optional) - an initial delay in millis to wait before the wait page is shown (returning wait as result code). Default is no initial delay.
+ * delaySleepInterval (optional) - only used with delay. Used for waking up at certain intervals to check if the background process is already done. Default is 100 millis.
+ *
+ *
+ *
+ *
+ *
+ *
Extending the interceptor:
+ *
+ *
+ *
+ *
+ *
+ * If you wish to make special preparations before and/or after the invocation of the background thread, you can extend
+ * the BackgroundProcess class and implement the beforeInvocation() and afterInvocation() methods. This may be useful
+ * for obtaining and releasing resources that the background process will need to execute successfully. To use your
+ * background process extension, extend ExecuteAndWaitInterceptor and implement the getNewBackgroundProcess() method.
+ *
+ *
+ *
+ *
Example code:
+ *
+ *
+ *
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="completeStack"/>
+ * <interceptor-ref name="execAndWait"/>
+ * <result name="wait">longRunningAction-wait.jsp</result>
+ * <result name="success">longRunningAction-success.jsp</result>
+ * </action>
+ *
+ * <%@ taglib prefix="s" uri="/struts" %>
+ * <html>
+ * <head>
+ * <title>Please wait</title>
+ * <meta http-equiv="refresh" content="5;url=<a:url includeParams="all" />"/>
+ * </head>
+ * <body>
+ * Please wait while we process your request.
+ * Click <a href="<a:url includeParams="all" />"></a> if this page does not reload automatically.
+ * </body>
+ * </html>
+ *
+ *
+ *
Example code2:
+ * This example will wait 2 second (2000 millis) before the wait page is shown to the user. Therefore
+ * if the long process didn't last long anyway the user isn't shown a wait page.
+ *
+ *
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="completeStack"/>
+ * <interceptor-ref name="execAndWait">
+ * <param name="delay">2000<param>
+ * <interceptor-ref>
+ * <result name="wait">longRunningAction-wait.jsp</result>
+ * <result name="success">longRunningAction-success.jsp</result>
+ * </action>
+ *
+ *
+ *
Example code3:
+ * This example will wait 1 second (1000 millis) before the wait page is shown to the user.
+ * And at every 50 millis this interceptor will check if the background process is done, if so
+ * it will return before the 1 second has elapsed, and the user isn't shown a wait page.
+ *
+ *
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="completeStack"/>
+ * <interceptor-ref name="execAndWait">
+ * <param name="delay">1000<param>
+ * <param name="delaySleepInterval">50<param>
+ * <interceptor-ref>
+ * <result name="wait">longRunningAction-wait.jsp</result>
+ * <result name="success">longRunningAction-success.jsp</result>
+ * </action>
+ *
+ *
+ *
+ *
+ */
+public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor {
+
+ private static final long serialVersionUID = -2754639196749652512L;
+
+ private static final Log LOG = LogFactory.getLog(ExecuteAndWaitInterceptor.class);
+
+ public static final String KEY = "__execWait";
+ public static final String WAIT = "wait";
+ protected int delay;
+ protected int delaySleepInterval = 100; // default sleep 100 millis before checking if background process is done
+ protected boolean executeAfterValidationPass = false;
+
+ private int threadPriority = Thread.NORM_PRIORITY;
+
+ /* (non-Javadoc)
+ * @see com.opensymphony.xwork2.interceptor.Interceptor#init()
+ */
+ public void init() {
+ }
+
+ /**
+ * Creates a new background process
+ *
+ * @param name The process name
+ * @param actionInvocation The action invocation
+ * @param threadPriority The thread priority
+ * @return The new process
+ */
+ protected BackgroundProcess getNewBackgroundProcess(String name, ActionInvocation actionInvocation, int threadPriority) {
+ return new BackgroundProcess(name + "BackgroundThread", actionInvocation, threadPriority);
+ }
+
+ /* (non-Javadoc)
+ * @see com.opensymphony.xwork2.interceptor.MethodFilterInterceptor#doIntercept(com.opensymphony.xwork2.ActionInvocation)
+ */
+ protected String doIntercept(ActionInvocation actionInvocation) throws Exception {
+ ActionProxy proxy = actionInvocation.getProxy();
+ String name = proxy.getActionName();
+ ActionContext context = actionInvocation.getInvocationContext();
+ Map session = context.getSession();
+
+ Boolean secondTime = true;
+ if (executeAfterValidationPass) {
+ secondTime = (Boolean) context.get(KEY);
+ if (secondTime == null) {
+ context.put(KEY, true);
+ secondTime = false;
+ } else {
+ secondTime = true;
+ }
+ }
+
+ synchronized (session) {
+ BackgroundProcess bp = (BackgroundProcess) session.get(KEY + name);
+
+ if (secondTime && bp == null) {
+ bp = getNewBackgroundProcess(name, actionInvocation, threadPriority);
+ session.put(KEY + name, bp);
+ performInitialDelay(bp); // first time let some time pass before showing wait page
+ secondTime = false;
+ }
+
+ if (!secondTime && bp != null && !bp.isDone()) {
+ actionInvocation.getStack().push(bp.getAction());
+ Map results = proxy.getConfig().getResults();
+ if (!results.containsKey(WAIT)) {
+ LOG.warn("ExecuteAndWait interceptor has detected that no result named 'wait' is available. " +
+ "Defaulting to a plain built-in wait page. It is highly recommend you " +
+ "provide an action-specific or global result named '" + WAIT +
+ "'! This requires FreeMarker support and won't work if you don't have it installed");
+ // no wait result? hmm -- let's try to do dynamically put it in for you!
+ ResultConfig rc = new ResultConfig(WAIT, "org.apache.struts2.views.freemarker.FreemarkerResult",
+ Collections.singletonMap("location", "/org/apache/struts2/interceptor/wait.ftl"));
+ results.put(WAIT, rc);
+ }
+
+ return WAIT;
+ } else if (!secondTime && bp != null && bp.isDone()) {
+ session.remove(KEY + name);
+ actionInvocation.getStack().push(bp.getAction());
+
+ // if an exception occured during action execution, throw it here
+ if (bp.getException() != null) {
+ throw bp.getException();
+ }
+
+ return bp.getResult();
+ } else {
+ // this is the first instance of the interceptor and there is no existing action
+ // already run in the background, so let's just let this pass through. We assume
+ // the action invocation will be run in the background on the subsequent pass through
+ // this interceptor
+ return actionInvocation.invoke();
+ }
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see com.opensymphony.xwork2.interceptor.Interceptor#destroy()
+ */
+ public void destroy() {
+ }
+
+ /**
+ * Performs the initial delay.
+ *
+ * When this interceptor is executed for the first time this methods handles any provided initial delay.
+ * An initial delay is a time in miliseconds we let the server wait before we continue.
+ * During the wait this interceptor will wake every 100 millis to check if the background
+ * process is done premature, thus if the job for some reason doesn't take to long the wait
+ * page is not shown to the user.
+ *
+ * @param bp the background process
+ * @throws InterruptedException is thrown by Thread.sleep
+ */
+ protected void performInitialDelay(BackgroundProcess bp) throws InterruptedException {
+ if (delay <= 0 || delaySleepInterval <= 0) {
+ return;
+ }
+
+ int steps = delay / delaySleepInterval;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Delaying for " + delay + " millis. (using " + steps + " steps)");
+ }
+ int step;
+ for (step = 0; step < steps && !bp.isDone(); step++) {
+ Thread.sleep(delaySleepInterval);
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Sleeping ended after " + step + " steps and the background process is " + (bp.isDone() ? " done" : " not done"));
+ }
+ }
+
+ /**
+ * Sets the thread priority of the background process.
+ *
+ * @param threadPriority the priority from Thread.XXX
+ */
+ public void setThreadPriority(int threadPriority) {
+ this.threadPriority = threadPriority;
+ }
+
+ /**
+ * Sets the initial delay in millis (msec).
+ *
+ * @param delay in millis. (0 for not used)
+ */
+ public void setDelay(int delay) {
+ this.delay = delay;
+ }
+
+ /**
+ * Sets the sleep interval in millis (msec) when performing the initial delay.
+ *
+ * @param delaySleepInterval in millis (0 for not used)
+ */
+ public void setDelaySleepInterval(int delaySleepInterval) {
+ this.delaySleepInterval = delaySleepInterval;
+ }
+
+ /**
+ * Whether to start the background process after the second pass (first being validation)
+ * or not
+ *
+ * @param executeAfterValidationPass the executeAfterValidationPass to set
+ */
+ public void setExecuteAfterValidationPass(boolean executeAfterValidationPass) {
+ this.executeAfterValidationPass = executeAfterValidationPass;
+ }
+
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
new file mode 100644
index 000000000..11c2a4abc
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
@@ -0,0 +1,369 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.ValidationAware;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.util.LocalizedTextUtil;
+
+/**
+ *
+ *
+ * Interceptor that is based off of {@link MultiPartRequestWrapper}, which is automatically applied for any request that
+ * includes a file. It adds the following parameters, where [File Name] is the name given to the file uploaded by the
+ * HTML form:
+ *
+ *
+ *
+ * [File Name] : File - the actual File
+ *
+ * [File Name]ContentType : String - the content type of the file
+ *
+ * [File Name]FileName : String - the actual name of the file uploaded (not the HTML name)
+ *
+ *
+ *
+ *
You can get access to these files by merely providing setters in your action that correspond to any of the three
+ * patterns above, such as setDocument(File document), setDocumentContentType(String contentType), etc.
+ * See the example code section.
+ *
+ *
This interceptor will add several field errors, assuming that the action implements {@link ValidationAware}.
+ * These error messages are based on several i18n values stored in struts-messages.properties, a default i18n file
+ * processed for all i18n requests. You can override the text of these messages by providing text for the following
+ * keys:
+ *
+ *
+ *
+ * struts.messages.error.uploading - a general error that occurs when the file could not be uploaded
+ *
+ * struts.messages.error.file.too.large - occurs when the uploaded file is too large
+ *
+ * struts.messages.error.content.type.not.allowed - occurs when the uploaded file does not match the expected
+ * content types specified
+ *
+ *
+ *
+ *
+ *
+ *
Interceptor parameters:
+ *
+ *
+ *
+ *
+ *
+ * maximumSize (optional) - the maximum size (in bytes) that the interceptor will allow a file reference to be set
+ * on the action. Note, this is not related to the various properties found in struts.properties.
+ * Default to approximately 2MB.
+ *
+ * allowedTypes (optional) - a comma separated list of content types (ie: text/html) that the interceptor will allow
+ * a file reference to be set on the action. If none is specified allow all types to be uploaded.
+ *
+ *
+ *
+ *
+ *
+ *
Extending the interceptor:
+ *
+ *
+ *
+ *
+ *
+ * You can extend this interceptor and override the {@link #acceptFile} method to provide more control over which files
+ * are supported and which are not.
+ *
+ *
+ *
+ *
Example code:
+ *
+ *
+ *
+ * <action name="doUpload" class="com.examples.UploadAction">
+ * <interceptor-ref name="fileUpload"/>
+ * <interceptor-ref name="basicStack"/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ *
+ *
+ * And then you need to set encoding multipart/form-data in the form where the user selects the file to upload.
+ *
+ * <a:form action="doUpload" method="post" enctype="multipart/form-data">
+ * <a:file name="upload" label="File"/>
+ * <a:submit/>
+ * </a:form>
+ *
+ *
+ * And then in your action code you'll have access to the File object if you provide setters according to the
+ * naming convention documented in the start.
+ *
+ *
+ * public com.examples.UploadAction implemements Action {
+ * private File file;
+ * private String contentType;
+ * private String filename;
+ *
+ * public void setUpload(File file) {
+ * this.file = file;
+ * }
+ *
+ * public void setUploadContentType(String contentType) {
+ * this.contentType = contentType;
+ * }
+ *
+ * public void setUploadFileName(String filename) {
+ * this.filename = filename;
+ * }
+ *
+ * ...
+ * }
+ *
+ *
+ *
+ */
+public class FileUploadInterceptor extends AbstractInterceptor {
+
+ private static final long serialVersionUID = -4764627478894962478L;
+
+ protected static final Log log = LogFactory.getLog(FileUploadInterceptor.class);
+ private static final String DEFAULT_DELIMITER = ",";
+ private static final String DEFAULT_MESSAGE = "no.message.found";
+
+ protected Long maximumSize;
+ protected String allowedTypes;
+ protected Set allowedTypesSet = Collections.EMPTY_SET;
+
+ /**
+ * Sets the allowed mimetypes
+ *
+ * @param allowedTypes A comma-delimited list of types
+ */
+ public void setAllowedTypes(String allowedTypes) {
+ this.allowedTypes = allowedTypes;
+
+ // set the allowedTypes as a collection for easier access later
+ allowedTypesSet = getDelimitedValues(allowedTypes);
+ }
+
+ /**
+ * Sets the maximum size of an uploaded file
+ *
+ * @param maximumSize The maximum size in bytes
+ */
+ public void setMaximumSize(Long maximumSize) {
+ this.maximumSize = maximumSize;
+ }
+
+ /* (non-Javadoc)
+ * @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
+ */
+ public String intercept(ActionInvocation invocation) throws Exception {
+ ActionContext ac = invocation.getInvocationContext();
+ HttpServletRequest request = (HttpServletRequest) ac.get(ServletActionContext.HTTP_REQUEST);
+
+ if (!(request instanceof MultiPartRequestWrapper)) {
+ if (log.isDebugEnabled()) {
+ ActionProxy proxy = invocation.getProxy();
+ log.debug(getTextMessage("struts.messages.bypass.request", new Object[]{proxy.getNamespace(), proxy.getActionName()}, ActionContext.getContext().getLocale()));
+ }
+
+ return invocation.invoke();
+ }
+
+ final Object action = invocation.getAction();
+ ValidationAware validation = null;
+
+ if (action instanceof ValidationAware) {
+ validation = (ValidationAware) action;
+ }
+
+ MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;
+
+ if (multiWrapper.hasErrors()) {
+ for (Iterator errorIter = multiWrapper.getErrors().iterator(); errorIter.hasNext();) {
+ String error = (String) errorIter.next();
+
+ if (validation != null) {
+ validation.addActionError(error);
+ }
+
+ log.error(error);
+ }
+ }
+
+ Map parameters = ac.getParameters();
+
+ // Bind allowed Files
+ Enumeration fileParameterNames = multiWrapper.getFileParameterNames();
+ while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {
+ // get the value of this input tag
+ String inputName = (String) fileParameterNames.nextElement();
+
+ // get the content type
+ String[] contentType = multiWrapper.getContentTypes(inputName);
+
+ if (isNonEmpty(contentType)) {
+ // get the name of the file from the input tag
+ String[] fileName = multiWrapper.getFileNames(inputName);
+
+ if (isNonEmpty(fileName)) {
+ // Get a File object for the uploaded File
+ File[] files = multiWrapper.getFiles(inputName);
+ if (files != null) {
+ for (int index = 0; index < files.length; index++) {
+ getTextMessage("struts.messages.current.file", new Object[]{inputName, contentType[index], fileName[index], files[index]}, ActionContext.getContext().getLocale());
+
+ if (acceptFile(files[0], contentType[0], inputName, validation, ac.getLocale())) {
+ parameters.put(inputName, files);
+ parameters.put(inputName + "ContentType", contentType);
+ parameters.put(inputName + "FileName", fileName);
+ }
+ }
+ }
+ } else {
+ log.error(getTextMessage("struts.messages.invalid.file", new Object[]{inputName}, ActionContext.getContext().getLocale()));
+ }
+ } else {
+ log.error(getTextMessage("struts.messages.invalid.content.type", new Object[]{inputName}, ActionContext.getContext().getLocale()));
+ }
+ }
+
+ // invoke action
+ String result = invocation.invoke();
+
+ // cleanup
+ fileParameterNames = multiWrapper.getFileParameterNames();
+ while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {
+ String inputValue = (String) fileParameterNames.nextElement();
+ File[] file = multiWrapper.getFiles(inputValue);
+ for (int index = 0; index < file.length; index++) {
+ File currentFile = file[index];
+ log.info(getTextMessage("struts.messages.removing.file", new Object[]{inputValue, currentFile}, ActionContext.getContext().getLocale()));
+
+ if ((currentFile != null) && currentFile.isFile()) {
+ currentFile.delete();
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Override for added functionality. Checks if the proposed file is acceptable based on contentType and size.
+ *
+ * @param file - proposed upload file.
+ * @param contentType - contentType of the file.
+ * @param inputName - inputName of the file.
+ * @param validation - Non-null ValidationAware if the action implements ValidationAware, allowing for better
+ * logging.
+ * @param locale
+ * @return true if the proposed file is acceptable by contentType and size.
+ */
+ protected boolean acceptFile(File file, String contentType, String inputName, ValidationAware validation, Locale locale) {
+ boolean fileIsAcceptable = false;
+
+ // If it's null the upload failed
+ if (file == null) {
+ String errMsg = getTextMessage("struts.messages.error.uploading", new Object[]{inputName}, locale);
+ if (validation != null) {
+ validation.addFieldError(inputName, errMsg);
+ }
+
+ log.error(errMsg);
+ } else if (maximumSize != null && maximumSize.longValue() < file.length()) {
+ String errMsg = getTextMessage("struts.messages.error.file.too.large", new Object[]{inputName, file.getName(), "" + file.length()}, locale);
+ if (validation != null) {
+ validation.addFieldError(inputName, errMsg);
+ }
+
+ log.error(errMsg);
+ } else if ((! allowedTypesSet.isEmpty()) && (!containsItem(allowedTypesSet, contentType))) {
+ String errMsg = getTextMessage("struts.messages.error.content.type.not.allowed", new Object[]{inputName, file.getName(), contentType}, locale);
+ if (validation != null) {
+ validation.addFieldError(inputName, errMsg);
+ }
+
+ log.error(errMsg);
+ } else {
+ fileIsAcceptable = true;
+ }
+
+ return fileIsAcceptable;
+ }
+
+ /**
+ * @param itemCollection - Collection of string items (all lowercase).
+ * @param key - Key to search for.
+ * @return true if itemCollection contains the key, false otherwise.
+ */
+ private static boolean containsItem(Collection itemCollection, String key) {
+ return itemCollection.contains(key.toLowerCase());
+ }
+
+ private static Set getDelimitedValues(String delimitedString) {
+ Set delimitedValues = new HashSet();
+ if (delimitedString != null) {
+ StringTokenizer stringTokenizer = new StringTokenizer(delimitedString, DEFAULT_DELIMITER);
+ while (stringTokenizer.hasMoreTokens()) {
+ String nextToken = stringTokenizer.nextToken().toLowerCase().trim();
+ if (nextToken.length() > 0) {
+ delimitedValues.add(nextToken);
+ }
+ }
+ }
+ return delimitedValues;
+ }
+
+ private static boolean isNonEmpty(Object[] objArray) {
+ boolean result = false;
+ for (int index = 0; index < objArray.length && !result; index++) {
+ if (objArray[index] != null) {
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ private String getTextMessage(String messageKey, Object[] args, Locale locale) {
+ if (args == null || args.length == 0) {
+ return LocalizedTextUtil.findText(this.getClass(), messageKey, locale);
+ } else {
+ return LocalizedTextUtil.findText(this.getClass(), messageKey, locale, DEFAULT_MESSAGE, args);
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/MessageStoreInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/MessageStoreInterceptor.java
new file mode 100644
index 000000000..5fecf94ad
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/MessageStoreInterceptor.java
@@ -0,0 +1,330 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ValidationAware;
+import com.opensymphony.xwork2.interceptor.Interceptor;
+
+/**
+ *
+ *
+ * An interceptor to store {@link ValidationAware} action's messages / errors and field errors into
+ * Http Session, such that it will be retrieveable at a later stage. This allows the action's message /
+ * errors and field errors to be available longer that just the particular http request.
+ *
+ *
+ *
+ * In the 'STORE' mode, the interceptor will store the {@link ValidationAware} action's message / errors
+ * and field errors into Http session.
+ *
+ *
+ *
+ * In the 'RETRIEVE' mode, the interceptor will retrieve the stored action's message / errors and field
+ * errors and put them back into the {@link ValidationAware} action.
+ *
+ *
+ *
+ * The interceptor does nothing in the 'NONE' mode, which is the default.
+ *
+ *
+ *
+ * The operation mode could be switched using :-
+ * 1] Setting the iterceptor parameter eg.
+ *
+ * <action name="submitApplication" ...>
+ * <interceptor-ref name="store">
+ * <param name="operationMode">l;STORE</param>
+ * </interceptor-ref>
+ * <interceptor-ref name="defaultStack" />
+ * ....
+ * </action>
+ *
+ *
+ * 2] Through request parameter (allowRequestParameterSwitch must be 'true' which is the default)
+ *
+ * // the request will have the operation mode in 'STORE'
+ * http://localhost:8080/context/submitApplication.action?operationMode=STORE
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * allowRequestParameterSwitch - To enable request parameter that could switch the operation mode
+ * of this interceptor.
+ * requestParameterSwitch - The request parameter that will indicate what mode this
+ * interceptor is in.
+ * operationMode - The operation mode this interceptor should be in
+ * (either 'STORE', 'RETRIEVE' or 'NONE'). 'NONE' being the default.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * The following method could be overriden :-
+ *
+ * getRequestOperationMode - get the operation mode of this interceptor based on the request parameters
+ * mergeCollection - merge two collections
+ * mergeMap - merge two map
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * <action name="submitApplication" ....>
+ * <interceptor-ref name="store">
+ * <param name="operationMode">STORE</param>
+ * </interceptor-ref>
+ * <interceptor-ref name="defaultStack" />
+ * <result name="input" type="redirect">applicationFailed.action</result>
+ * <result type="dispatcher">applicationSuccess.jsp</result>
+ * </action>
+ *
+ * <action name="applicationFailed" ....>
+ * <interceptor-ref name="store">
+ * <param name="operationMode">RETRIEVE</param>
+ * </interceptor-ref>
+ * <result>applicationFailed.jsp</result>
+ * </action>
+ *
+ *
+ *
+ *
+ *
+ *
+ * With the example above, 'submitApplication.action' will have the action messages / errors / field errors stored
+ * in the Http Session. Later when needed, (in this case, when 'applicationFailed.action' is fired, it
+ * will get the action messages / errors / field errors stored in the Http Session and put them back into
+ * the action.
+ *
+ *
+ *
+ * @version $Date$ $Id$
+ */
+public class MessageStoreInterceptor implements Interceptor {
+
+ private static final long serialVersionUID = 4491997514314242420L;
+
+ private static final Log _log = LogFactory.getLog(MessageStoreInterceptor.class);
+
+
+ public static final String STORE_MODE = "STORE";
+ public static final String RETRIEVE_MODE = "RETRIEVE";
+ public static final String NONE = "NONE";
+
+ private boolean allowRequestParameterSwitch = true;
+ private String requestParameterSwitch = "operationMode";
+ private String operationMode = NONE;
+
+ public static String fieldErrorsSessionKey = "__MessageStoreInterceptor_FieldErrors_SessionKey";
+ public static String actionErrorsSessionKey = "__MessageStoreInterceptor_ActionErrors_SessionKey";
+ public static String actionMessagesSessionKey = "__MessageStoreInterceptor_ActionMessages_SessionKey";
+
+
+
+ public void setAllowRequestParameterSwitch(boolean allowRequestParameterSwitch) {
+ this.allowRequestParameterSwitch = allowRequestParameterSwitch;
+ }
+ public boolean getAllowRequestParameterSwitch() {
+ return this.allowRequestParameterSwitch;
+ }
+
+
+ public void setRequestParameterSwitch(String requestParameterSwitch) {
+ this.requestParameterSwitch = requestParameterSwitch;
+ }
+ public String getRequestParameterSwitch() {
+ return this.requestParameterSwitch;
+ }
+
+
+
+ public void setOperationMode(String operationMode) {
+ this.operationMode = operationMode;
+ }
+ public String getOperationModel() {
+ return this.operationMode;
+ }
+
+
+ public void destroy() {
+ }
+
+ public void init() {
+ }
+
+ public String intercept(ActionInvocation invocation) throws Exception {
+ _log.debug("entering MessageStoreInterceptor ...");
+
+ before(invocation);
+ String result = invocation.invoke();
+ after(invocation, result);
+
+ _log.debug("exit executing MessageStoreInterceptor");
+ return result;
+ }
+
+ /**
+ * Handle the retrieving of field errors / action messages / field errors, which is
+ * done before action invocation, and the operationMode is 'RETRIEVE'.
+ *
+ * @param invocation
+ * @throws Exception
+ */
+ protected void before(ActionInvocation invocation) throws Exception {
+ String reqOperationMode = getRequestOperationMode(invocation);
+
+ if (RETRIEVE_MODE.equalsIgnoreCase(reqOperationMode) ||
+ RETRIEVE_MODE.equalsIgnoreCase(operationMode)) {
+
+ Object action = invocation.getAction();
+ if (action instanceof ValidationAware) {
+ // retrieve error / message from session
+ Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);
+ ValidationAware validationAwareAction = (ValidationAware) action;
+
+ _log.debug("retrieve error / message from session to populate into action ["+action+"]");
+
+ Collection actionErrors = (Collection) session.get(actionErrorsSessionKey);
+ Collection actionMessages = (Collection) session.get(actionMessagesSessionKey);
+ Map fieldErrors = (Map) session.get(fieldErrorsSessionKey);
+
+ if (actionErrors != null && actionErrors.size() > 0) {
+ Collection mergedActionErrors = mergeCollection(validationAwareAction.getActionErrors(), actionErrors);
+ validationAwareAction.setActionErrors(mergedActionErrors);
+ }
+
+ if (actionMessages != null && actionMessages.size() > 0) {
+ Collection mergedActionMessages = mergeCollection(validationAwareAction.getActionMessages(), actionMessages);
+ validationAwareAction.setActionMessages(mergedActionMessages);
+ }
+
+ if (fieldErrors != null && fieldErrors.size() > 0) {
+ Map mergedFieldErrors = mergeMap(validationAwareAction.getFieldErrors(), fieldErrors);
+ validationAwareAction.setFieldErrors(mergedFieldErrors);
+ }
+ session.remove(actionErrorsSessionKey);
+ session.remove(actionMessagesSessionKey);
+ session.remove(fieldErrorsSessionKey);
+ }
+ }
+ }
+
+ /**
+ * Handle the storing of field errors / action messages / field errors, which is
+ * done after action invocation, and the operationMode is in 'STORE'.
+ *
+ * @param invocation
+ * @param result
+ * @throws Exception
+ */
+ protected void after(ActionInvocation invocation, String result) throws Exception {
+
+ String reqOperationMode = getRequestOperationMode(invocation);
+ if (STORE_MODE.equalsIgnoreCase(reqOperationMode) ||
+ STORE_MODE.equalsIgnoreCase(operationMode)) {
+
+ Object action = invocation.getAction();
+ if (action instanceof ValidationAware) {
+ // store error / messages into session
+ Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);
+
+ _log.debug("store action ["+action+"] error/messages into session ");
+
+ ValidationAware validationAwareAction = (ValidationAware) action;
+ session.put(actionErrorsSessionKey, validationAwareAction.getActionErrors());
+ session.put(actionMessagesSessionKey, validationAwareAction.getActionMessages());
+ session.put(fieldErrorsSessionKey, validationAwareAction.getFieldErrors());
+ }
+ else {
+ _log.debug("Action ["+action+"] is not ValidationAware, no message / error that are storeable");
+ }
+ }
+ }
+
+
+ /**
+ * Get the operationMode through request paramter, if allowRequestParameterSwitch
+ * is 'true', else it simply returns 'NONE', meaning its neither in the 'STORE_MODE' nor
+ * 'RETRIEVE_MODE'.
+ *
+ * @return String
+ */
+ protected String getRequestOperationMode(ActionInvocation invocation) {
+ String reqOperationMode = NONE;
+ if (allowRequestParameterSwitch) {
+ Map reqParams = (Map) invocation.getInvocationContext().get(ActionContext.PARAMETERS);
+ boolean containsParameter = reqParams.containsKey(requestParameterSwitch);
+ if (containsParameter) {
+ String[] reqParamsArr = (String[]) reqParams.get(requestParameterSwitch);
+ if (reqParamsArr != null && reqParamsArr.length > 0) {
+ reqOperationMode = reqParamsArr[0];
+ }
+ }
+ }
+ return reqOperationMode;
+ }
+
+ /**
+ * Merge col1 and col2 and return the composite
+ * Collection.
+ *
+ * @param col1
+ * @param col2
+ * @return Collection
+ */
+ protected Collection mergeCollection(Collection col1, Collection col2) {
+ Collection _col1 = (col1 == null ? new ArrayList() : col1);
+ Collection _col2 = (col2 == null ? new ArrayList() : col2);
+ _col1.addAll(_col2);
+ return _col1;
+ }
+
+ /**
+ * Merge map1 and map2 and return the composite
+ * Map
+ *
+ * @param map1
+ * @param map2
+ * @return Map
+ */
+ protected Map mergeMap(Map map1, Map map2) {
+ Map _map1 = (map1 == null ? new LinkedHashMap() : map1);
+ Map _map2 = (map2 == null ? new LinkedHashMap() : map2);
+ _map1.putAll(_map2);
+ return _map1;
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/NoParameters.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/NoParameters.java
new file mode 100644
index 000000000..5fb3eb877
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/NoParameters.java
@@ -0,0 +1,29 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+
+/**
+ * This marker interface should be implemented by actions that do not want any parameters set on
+ * them automatically. This may be useful if one is using the action tag and want to supply
+ * the parameters to the action manually using the param tag. It may also be useful if one for
+ * security reasons wants to make sure that parameters cannot be set by malicious users.
+ *
+ */
+public interface NoParameters extends com.opensymphony.xwork2.interceptor.NoParameters {
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/ParameterAware.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/ParameterAware.java
new file mode 100644
index 000000000..a1071c48a
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/ParameterAware.java
@@ -0,0 +1,42 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.util.Map;
+
+
+/**
+ * This interface gives actions an alternative way of receiving input parameters. The map will
+ * contain all input parameters as name/value entries. Actions that need this should simply implement it.
+ *
+ * One common use for this is to have the action propagate parameters to internally instantiated data
+ * objects.
+ *
+ * Note that all parameter values for a given name will be returned, so the type of the objects in
+ * the map is java.lang.String[] .
+ *
+ */
+public interface ParameterAware {
+
+ /**
+ * Sets the map of input parameters in the implementing class.
+ *
+ * @param parameters a Map of parameters (name/value Strings).
+ */
+ public void setParameters(Map parameters);
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/PrincipalAware.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/PrincipalAware.java
new file mode 100644
index 000000000..891a86092
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/PrincipalAware.java
@@ -0,0 +1,30 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+/**
+ * Actions that want access to the Principal information from HttpServletRequest object
+ * should implement this interface.
+ *
+ * This interface is only relevant if the Action is used in a servlet environment.
+ * By using this interface you will not become tied to servlet environment.
+ *
+ */
+public interface PrincipalAware {
+ void setPrincipalProxy(PrincipalProxy principalProxy);
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/PrincipalProxy.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/PrincipalProxy.java
new file mode 100644
index 000000000..e513071be
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/PrincipalProxy.java
@@ -0,0 +1,86 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.security.Principal;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Proxy class used together with PrincipalAware interface. It allows to get indirect access to
+ * HttpServletRequest Principal related methods.
+ *
+ */
+public class PrincipalProxy {
+ private HttpServletRequest request;
+
+ /**
+ * Constructs a proxy
+ *
+ * @param request The underlying request
+ */
+ public PrincipalProxy(HttpServletRequest request) {
+ this.request = request;
+ }
+
+ /**
+ * True if the user is in the given role
+ *
+ * @param role The role
+ * @return True if the user is in that role
+ */
+ public boolean isUserInRole(String role) {
+ return request.isUserInRole(role);
+ }
+
+ /**
+ * Gets the user principal
+ *
+ * @return The principal
+ */
+ public Principal getUserPrincipal() {
+ return request.getUserPrincipal();
+ }
+
+ /**
+ * Gets the user id
+ *
+ * @return The user id
+ */
+ public String getRemoteUser() {
+ return request.getRemoteUser();
+ }
+
+ /**
+ * Is the request using https?
+ *
+ * @return True if using https
+ */
+ public boolean isRequestSecure() {
+ return request.isSecure();
+ }
+
+ /**
+ * Gets the request
+ *
+ * @return The request
+ */
+ public HttpServletRequest getRequest() {
+ return request;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/ProfilingActivationInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/ProfilingActivationInterceptor.java
new file mode 100644
index 000000000..1e619cf05
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/ProfilingActivationInterceptor.java
@@ -0,0 +1,63 @@
+/*
+ * $Id: CreateSessionInterceptor.java 439747 2006-09-03 09:22:46Z mrdon $
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import org.apache.struts2.dispatcher.Dispatcher;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
+
+/**
+ * Allows profiling to be enabled or disabled via request parameters, when
+ * devMode is enabled.
+ */
+public class ProfilingActivationInterceptor extends AbstractInterceptor {
+
+ private String profilingKey = "profiling";
+
+ /**
+ * @return the profilingKey
+ */
+ public String getProfilingKey() {
+ return profilingKey;
+ }
+
+ /**
+ * @param profilingKey the profilingKey to set
+ */
+ public void setProfilingKey(String profilingKey) {
+ this.profilingKey = profilingKey;
+ }
+
+ @Override
+ public String intercept(ActionInvocation invocation) throws Exception {
+ if (Dispatcher.getInstance().isDevMode()) {
+ Object val = invocation.getInvocationContext().getParameters().get(profilingKey);
+ if (val != null) {
+ String sval = (val instanceof String ? (String)val : ((String[])val)[0]);
+ boolean enable = "yes".equalsIgnoreCase(sval) || "true".equalsIgnoreCase(sval);
+ UtilTimerStack.setActive(enable);
+ invocation.getInvocationContext().getParameters().remove(profilingKey);
+ }
+ }
+ return invocation.invoke();
+
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/RequestAware.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/RequestAware.java
new file mode 100644
index 000000000..77b42d46b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/RequestAware.java
@@ -0,0 +1,38 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.util.Map;
+
+/**
+ * Actions that want access to the current serlvet request attributes should implement this interface.
+ *
+ * This interface is only relevant if the Action is used in a servlet environment.
+ *
+ * Note that using this interface makes the Action tied to a servlet environment, so it should be
+ * avoided if possible since things like unit testing will become more difficult.
+ */
+public interface RequestAware {
+
+ /**
+ * Sets the Map of request attributes in the implementing class.
+ *
+ * @param request a Map of HTTP request attribute name/value pairs.
+ */
+ public void setRequest(Map request);
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/ScopeInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/ScopeInterceptor.java
new file mode 100644
index 000000000..bcf63ab89
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/ScopeInterceptor.java
@@ -0,0 +1,441 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsException;
+import org.apache.struts2.dispatcher.SessionMap;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.interceptor.PreResultListener;
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ *
+ *
+ * This is designed to solve a few simple issues related to wizard-like functionality in Struts. One of those issues is
+ * that some applications have a application-wide parameters commonly used, such pageLen (used for records per
+ * page). Rather than requiring that each action check if such parameters are supplied, this interceptor can look for
+ * specified parameters and pull them out of the session.
+ *
+ *
This works by setting listed properties at action start with values from session/application attributes keyed
+ * after the action's class, the action's name, or any supplied key. After action is executed all the listed properties
+ * are taken back and put in session or application context.
+ *
+ *
To make sure that each execution of the action is consistent it makes use of session-level locking. This way it
+ * guarantees that each action execution is atomic at the session level. It doesn't guarantee application level
+ * consistency however there has yet to be enough reasons to do so. Application level consistency would also be a big
+ * performance overkill.
+ *
+ *
Note that this interceptor takes a snapshot of action properties just before result is presented (using a {@link
+ * PreResultListener}), rather than after action is invoked. There is a reason for that: At this moment we know that
+ * action's state is "complete" as it's values may depend on the rest of the stack and specifically - on the values of
+ * nested interceptors.
+ *
+ *
+ *
+ *
Interceptor parameters:
+ *
+ *
+ *
+ *
+ *
+ * session - a list of action properties to be bound to session scope
+ *
+ * application - a list of action properties to be bound to application scope
+ *
+ * key - a session/application attribute key prefix, can contain following values:
+ *
+ *
+ *
+ * CLASS - that creates a unique key prefix based on action namespace and action class, it's a default value
+ *
+ * ACTION - creates a unique key prefix based on action namespace and action name
+ *
+ * any other value is taken literally as key prefix
+ *
+ *
+ *
+ * type - with one of the following
+ *
+ *
+ *
+ * start - means it's a start action of the wizard-like action sequence and all session scoped properties are reset
+ * to their defaults
+ *
+ * end - means that session scoped properties are removed from session after action is run
+ *
+ * any other value or no value means that it's in-the-middle action that is set with session properties before it's
+ * executed, and it's properties are put back to session after execution
+ *
+ *
+ *
+ * sessionReset - boolean value causing all session values to be reset to action's default values or application
+ * scope values, note that it is similliar to type="start" and in fact it does the same, but in our team it is sometimes
+ * semantically preferred. We use session scope in two patterns - sometimes there are wizzard-like action sequences that
+ * have start and end, and sometimes we just want simply reset current session values.
+ *
+ *
+ *
+ *
+ *
+ *
Extending the interceptor:
+ *
+ *
+ *
+ *
+ *
+ * There are no know extension points for this interceptor.
+ *
+ *
+ *
+ *
Example code:
+ *
+ *
+ *
+ * <!-- As the filter and orderBy parameters are common for all my browse-type actions,
+ * you can move control to the scope interceptor. In the session parameter you can list
+ * action properties that are going to be automatically managed over session. You can
+ * do the same for application-scoped variables-->
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="basicStack"/>
+ * <interceptor-ref name="hibernate"/>
+ * <interceptor-ref name="scope">
+ * <param name="session">filter,orderBy</param>
+ * <param name="autoCreateSession">true</param>
+ * </interceptor-ref>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ *
+ *
+ *
+ */
+public class ScopeInterceptor extends AbstractInterceptor implements PreResultListener {
+
+ private static final long serialVersionUID = 9120762699600054395L;
+
+ private static final Log LOG = LogFactory.getLog(ScopeInterceptor.class);
+
+ private String[] application = null;
+ private String[] session = null;
+ private String key;
+ private String type = null;
+ private boolean autoCreateSession = true;
+ private String sessionReset = "session.reset";
+ private boolean reset = false;
+
+ /**
+ * Sets a list of application scoped properties
+ *
+ * @param s A comma-delimited list
+ */
+ public void setApplication(String s) {
+ if (s != null) {
+ application = s.split(" *, *");
+ }
+ }
+
+ /**
+ * Sets a list of session scoped properties
+ *
+ * @param s A comma-delimited list
+ */
+ public void setSession(String s) {
+ if (s != null) {
+ session = s.split(" *, *");
+ }
+ }
+
+ /**
+ * Sets if the session should be automatically created
+ *
+ * @param value True if it should be created
+ */
+ public void setAutoCreateSession(String value) {
+ if (value != null && value.length() > 0) {
+ this.autoCreateSession = new Boolean(value).booleanValue();
+ }
+ }
+
+ private String getKey(ActionInvocation invocation) {
+ ActionProxy proxy = invocation.getProxy();
+ if (key == null || "CLASS".equals(key)) {
+ return "struts.ScopeInterceptor:" + proxy.getAction().getClass();
+ } else if ("ACTION".equals(key)) {
+ return "struts.ScopeInterceptor:" + proxy.getNamespace() + ":" + proxy.getActionName();
+ }
+ return key;
+ }
+
+ /**
+ * The constructor
+ */
+ public ScopeInterceptor() {
+ super();
+ }
+
+
+ private static final Object NULL = new Object() {
+ public String toString() {
+ return "NULL";
+ }
+ };
+
+ private static final Object nullConvert(Object o) {
+ if (o == null) {
+ return NULL;
+ }
+
+ if (o == NULL) {
+ return null;
+ }
+
+ return o;
+ }
+
+
+ private static Map locks = new IdentityHashMap();
+
+ static final void lock(Object o, ActionInvocation invocation) throws Exception {
+ synchronized (o) {
+ int count = 3;
+ Object previous = null;
+ while ((previous = locks.get(o)) != null) {
+ if (previous == invocation) {
+ return;
+ }
+ if (count-- <= 0) {
+ locks.remove(o);
+ o.notify();
+
+ throw new StrutsException("Deadlock in session lock");
+ }
+ o.wait(10000);
+ }
+ ;
+ locks.put(o, invocation);
+ }
+ }
+
+ static final void unlock(Object o) {
+ synchronized (o) {
+ locks.remove(o);
+ o.notify();
+ }
+ }
+
+ protected void after(ActionInvocation invocation, String result) throws Exception {
+ Map ses = ActionContext.getContext().getSession();
+ if ( ses != null) {
+ unlock(ses);
+ }
+ }
+
+
+ protected void before(ActionInvocation invocation) throws Exception {
+ invocation.addPreResultListener(this);
+ Map ses = ActionContext.getContext().getSession();
+ if (ses == null && autoCreateSession) {
+ ses = new SessionMap(ServletActionContext.getRequest());
+ ActionContext.getContext().setSession(ses);
+ }
+
+ if ( ses != null) {
+ lock(ses, invocation);
+ }
+
+ String key = getKey(invocation);
+ Map app = ActionContext.getContext().getApplication();
+ final ValueStack stack = ActionContext.getContext().getValueStack();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("scope interceptor before");
+ }
+
+ if (application != null)
+ for (int i = 0; i < application.length; i++) {
+ String string = application[i];
+ Object attribute = app.get(key + string);
+ if (attribute != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("application scoped variable set " + string + " = " + String.valueOf(attribute));
+ }
+
+ stack.setValue(string, nullConvert(attribute));
+ }
+ }
+
+ if (ActionContext.getContext().getParameters().get(sessionReset) != null) {
+ return;
+ }
+
+ if (reset) {
+ return;
+ }
+
+ if (ses == null) {
+ LOG.debug("No HttpSession created... Cannot set session scoped variables");
+ return;
+ }
+
+ if (session != null && (!"start".equals(type))) {
+ for (int i = 0; i < session.length; i++) {
+ String string = session[i];
+ Object attribute = ses.get(key + string);
+ if (attribute != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("session scoped variable set " + string + " = " + String.valueOf(attribute));
+ }
+ stack.setValue(string, nullConvert(attribute));
+ }
+ }
+ }
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ /* (non-Javadoc)
+ * @see com.opensymphony.xwork2.interceptor.PreResultListener#beforeResult(com.opensymphony.xwork2.ActionInvocation, java.lang.String)
+ */
+ public void beforeResult(ActionInvocation invocation, String resultCode) {
+ String key = getKey(invocation);
+ Map app = ActionContext.getContext().getApplication();
+ final ValueStack stack = ActionContext.getContext().getValueStack();
+
+ if (application != null)
+ for (int i = 0; i < application.length; i++) {
+ String string = application[i];
+ Object value = stack.findValue(string);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("application scoped variable saved " + string + " = " + String.valueOf(value));
+ }
+
+ //if( value != null)
+ app.put(key + string, nullConvert(value));
+ }
+
+ boolean ends = "end".equals(type);
+
+ Map ses = ActionContext.getContext().getSession();
+ if (ses != null) {
+
+ if (session != null) {
+ for (int i = 0; i < session.length; i++) {
+ String string = session[i];
+ if (ends) {
+ ses.remove(key + string);
+ } else {
+ Object value = stack.findValue(string);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("session scoped variable saved " + string + " = " + String.valueOf(value));
+ }
+
+ // Null value should be scoped too
+ //if( value != null)
+ ses.put(key + string, nullConvert(value));
+ }
+ }
+ }
+ unlock(ses);
+ } else {
+ LOG.debug("No HttpSession created... Cannot save session scoped variables.");
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("scope interceptor after (before result)");
+ }
+ }
+
+ /**
+ * @return The type of scope operation, "start" or "end"
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Sets the type of scope operation
+ *
+ * @param type Either "start" or "end"
+ */
+ public void setType(String type) {
+ type = type.toLowerCase();
+ if ("start".equals(type) || "end".equals(type)) {
+ this.type = type;
+ } else {
+ throw new IllegalArgumentException("Only start or end are allowed arguments for type");
+ }
+ }
+
+ /**
+ * @return Gets the session reset parameter name
+ */
+ public String getSessionReset() {
+ return sessionReset;
+ }
+
+ /**
+ * @param sessionReset The session reset parameter name
+ */
+ public void setSessionReset(String sessionReset) {
+ this.sessionReset = sessionReset;
+ }
+
+ /* (non-Javadoc)
+ * @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
+ */
+ public String intercept(ActionInvocation invocation) throws Exception {
+ String result = null;
+ Map ses = ActionContext.getContext().getSession();
+ before(invocation);
+ try {
+ result = invocation.invoke();
+ after(invocation, result);
+ } finally {
+ if (ses != null) {
+ unlock(ses);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * @return True if the scope is reset
+ */
+ public boolean isReset() {
+ return reset;
+ }
+
+ /**
+ * @param reset True if the scope should be reset
+ */
+ public void setReset(boolean reset) {
+ this.reset = reset;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/ServletConfigInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/ServletConfigInterceptor.java
new file mode 100644
index 000000000..96b1039ba
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/ServletConfigInterceptor.java
@@ -0,0 +1,158 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.util.ServletContextAware;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+
+
+/**
+ *
+ *
+ * An interceptor which sets action properties based on the interfaces an action implements. For example, if the action
+ * implements {@link ParameterAware} then the action context's parameter map will be set on it.
+ *
+ *
This interceptor is designed to set all properties an action needs if it's aware of servlet parameters, the
+ * servlet context, the session, etc. Interfaces that it supports are:
+ *
+ *
+ *
+ * {@link ServletContextAware}
+ *
+ * {@link ServletRequestAware}
+ *
+ * {@link ServletResponseAware}
+ *
+ * {@link ParameterAware}
+ *
+ * {@link RequestAware}
+ *
+ * {@link SessionAware}
+ *
+ * {@link ApplicationAware}
+ *
+ * {@link PrincipalAware}
+ *
+ *
+ *
+ *
+ *
+ *
Interceptor parameters:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
Extending the interceptor:
+ *
+ *
+ *
+ *
+ *
+ * There are no known extension points for this interceptor.
+ *
+ *
+ *
+ *
Example code:
+ *
+ *
+ *
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="servlet-config"/>
+ * <interceptor-ref name="basicStack"/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ *
+ *
+ *
+ * @see ServletContextAware
+ * @see ServletRequestAware
+ * @see ServletResponseAware
+ * @see ParameterAware
+ * @see SessionAware
+ * @see ApplicationAware
+ * @see PrincipalAware
+ */
+public class ServletConfigInterceptor extends AbstractInterceptor implements StrutsStatics {
+
+ private static final long serialVersionUID = 605261777858676638L;
+
+ /**
+ * Sets action properties based on the interfaces an action implements. Things like application properties,
+ * parameters, session attributes, etc are set based on the implementing interface.
+ *
+ * @param invocation an encapsulation of the action execution state.
+ * @throws Exception if an error occurs when setting action properties.
+ */
+ public String intercept(ActionInvocation invocation) throws Exception {
+ final Object action = invocation.getAction();
+ final ActionContext context = invocation.getInvocationContext();
+
+ if (action instanceof ServletRequestAware) {
+ HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
+ ((ServletRequestAware) action).setServletRequest(request);
+ }
+
+ if (action instanceof ServletResponseAware) {
+ HttpServletResponse response = (HttpServletResponse) context.get(HTTP_RESPONSE);
+ ((ServletResponseAware) action).setServletResponse(response);
+ }
+
+ if (action instanceof ParameterAware) {
+ ((ParameterAware) action).setParameters(context.getParameters());
+ }
+
+ if (action instanceof RequestAware) {
+ ((RequestAware) action).setRequest((Map) context.get("request"));
+ }
+
+ if (action instanceof SessionAware) {
+ ((SessionAware) action).setSession(context.getSession());
+ }
+
+ if (action instanceof ApplicationAware) {
+ ((ApplicationAware) action).setApplication(context.getApplication());
+ }
+
+ if (action instanceof PrincipalAware) {
+ HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
+ ((PrincipalAware) action).setPrincipalProxy(new PrincipalProxy(request));
+ }
+ if (action instanceof ServletContextAware) {
+ ServletContext servletContext = (ServletContext) context.get(SERVLET_CONTEXT);
+ ((ServletContextAware) action).setServletContext(servletContext);
+ }
+ return invocation.invoke();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/ServletRequestAware.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/ServletRequestAware.java
new file mode 100644
index 000000000..d372a7089
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/ServletRequestAware.java
@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * All Actions that want to have access to the servlet request object must implement this interface.
+ *
+ * This interface is only relevant if the Action is used in a servlet environment.
+ *
+ * Note that using this interface makes the Action tied to a servlet environment, so it should be
+ * avoided if possible since things like unit testing will become more difficult.
+ *
+ */
+public interface ServletRequestAware {
+
+ /**
+ * Sets the HTTP request object in implementing classes.
+ *
+ * @param request the HTTP request.
+ */
+ public void setServletRequest(HttpServletRequest request);
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/ServletResponseAware.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/ServletResponseAware.java
new file mode 100644
index 000000000..5fef895ee
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/ServletResponseAware.java
@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * All Actions that want to have access to the servlet response object must implement this interface.
+ *
+ * This interface is only relevant if the Action is used in a servlet environment.
+ *
+ * Note that using this interface makes the Action tied to a servlet environment, so it should be
+ * avoided if possible since things like unit testing will become more difficult.
+ *
+ */
+public interface ServletResponseAware {
+
+ /**
+ * Sets the HTTP response object in implementing classes.
+ *
+ * @param response the HTTP response.
+ */
+ public void setServletResponse(HttpServletResponse response);
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/SessionAware.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/SessionAware.java
new file mode 100644
index 000000000..f21b999de
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/SessionAware.java
@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.util.Map;
+
+
+/**
+ * Actions that want access to the user's HTTP session should implement this interface.
+ *
+ * This interface is only relevant if the Action is used in a servlet environment.
+ *
+ * Note that using this interface makes the Action tied to a servlet environment, so it should be
+ * avoided if possible since things like unit testing will become more difficult.
+ *
+ */
+public interface SessionAware {
+
+ /**
+ * Sets the Map of session attributes in the implementing class.
+ *
+ * @param session a Map of HTTP session attribute name/value pairs.
+ */
+ public void setSession(Map session);
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/StrutsConversionErrorInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/StrutsConversionErrorInterceptor.java
new file mode 100644
index 000000000..9938900ce
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/StrutsConversionErrorInterceptor.java
@@ -0,0 +1,122 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor;
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ *
+ *
+ * This interceptor extends {@link ConversionErrorInterceptor} but only adds conversion errors from the ActionContext to
+ * the field errors of the action if the field value is not null, "", or {""} (a size 1 String array with only an empty
+ * String). See {@link ConversionErrorInterceptor} for more information, as well as the Type Conversion documentation.
+ *
+ *
+ *
+ *
Interceptor parameters:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
Extending the interceptor:
+ *
+ *
+ *
+ *
+ *
+ * There are no known extension points for this interceptor.
+ *
+ *
+ *
+ *
+ *
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="params"/>
+ * <interceptor-ref name="conversionError"/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ *
+ *
+ *
+ * @see com.opensymphony.xwork2.ActionContext#getConversionErrors()
+ * @see ConversionErrorInterceptor
+ */
+public class StrutsConversionErrorInterceptor extends ConversionErrorInterceptor {
+
+ private static final long serialVersionUID = 2759744840082921602L;
+
+ protected Object getOverrideExpr(ActionInvocation invocation, Object value) {
+ ValueStack stack = invocation.getStack();
+
+ try {
+ stack.push(value);
+
+ return "'" + stack.findValue("top", String.class) + "'";
+ } finally {
+ stack.pop();
+ }
+ }
+
+ /**
+ * Returns false if the value is null, "", or {""} (array of size 1 with a blank element). Returns
+ * true otherwise.
+ *
+ * @param propertyName the name of the property to check.
+ * @param value the value to error check.
+ * @return false if the value is null, "", or {""}, true otherwise.
+ */
+ protected boolean shouldAddError(String propertyName, Object value) {
+ if (value == null) {
+ return false;
+ }
+
+ if ("".equals(value)) {
+ return false;
+ }
+
+ if (value instanceof String[]) {
+ String[] array = (String[]) value;
+
+ if (array.length == 0) {
+ return false;
+ }
+
+ if (array.length > 1) {
+ return true;
+ }
+
+ String str = array[0];
+
+ if ("".equals(str)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/TokenInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/TokenInterceptor.java
new file mode 100644
index 000000000..515c08388
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/TokenInterceptor.java
@@ -0,0 +1,171 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.util.Map;
+
+import org.apache.struts2.util.TokenHelper;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ValidationAware;
+import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
+import com.opensymphony.xwork2.util.LocalizedTextUtil;
+
+/**
+ *
+ *
+ * Ensures that only one request per token is processed. This interceptor can make sure that back buttons and double
+ * clicks don't cause un-intended side affects. For example, you can use this to prevent careless users who might double
+ * click on a "checkout" button at an online store. This interceptor uses a fairly primitive technique for when an
+ * invalid token is found: it returns the result invalid.token , which can be mapped in your action configuration.
+ * A more complex implementation, {@link TokenSessionStoreInterceptor}, can provide much better logic for when invalid
+ * tokens are found.
+ *
+ *
+ *
+ * Note: To set a token in your form, you should use the token tag . This tag is required and must be used
+ * in the forms that submit to actions protected by this interceptor. Any request that does not provide a token (using
+ * the token tag) will be processed as a request with an invalid token.
+ *
+ *
+ *
+ * Internationalization Note: The following key could be used to internationalized the action errors generated
+ * by this token interceptor
+ *
+ *
+ * struts.messages.invalid.token
+ *
+ *
+ *
+ *
+ * NOTE: As this method extends off MethodFilterInterceptor, it is capable of
+ * deciding if it is applicable only to selective methods in the action class. See
+ * MethodFilterInterceptor for more info.
+ *
+ *
+ *
+ *
Interceptor parameters:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
Extending the interceptor:
+ *
+ *
+ *
+ *
+ *
+ * While not very common for users to extend, this interceptor is extended by the {@link TokenSessionStoreInterceptor}.
+ * The {@link #handleInvalidToken} and {@link #handleValidToken} methods are protected and available for more
+ * interesting logic, such as done with the token session interceptor.
+ *
+ *
+ *
+ *
Example code:
+ *
+ *
+ *
+ *
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="token"/>
+ * <interceptor-ref name="basicStack"/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ *
+ * <-- In this case, myMethod of the action class will not
+ * get checked for invalidity of token -->
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="token">
+ * <param name="excludeMethods">myMethod</param>
+ * </interceptor-ref name="token"/>
+ * <interceptor-ref name="basicStack"/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ *
+ *
+ *
+ *
+ * @see TokenSessionStoreInterceptor
+ * @see TokenHelper
+ */
+public class TokenInterceptor extends MethodFilterInterceptor {
+
+ private static final long serialVersionUID = -6680894220590585506L;
+
+ public static final String INVALID_TOKEN_CODE = "invalid.token";
+
+ /**
+ * @see com.opensymphony.xwork2.interceptor.MethodFilterInterceptor#doIntercept(com.opensymphony.xwork2.ActionInvocation)
+ */
+ protected String doIntercept(ActionInvocation invocation) throws Exception {
+ if (log.isDebugEnabled()) {
+ log.debug("Intercepting invocation to check for valid transaction token.");
+ }
+
+ Map session = ActionContext.getContext().getSession();
+
+ synchronized (session) {
+ if (!TokenHelper.validToken()) {
+ return handleInvalidToken(invocation);
+ }
+
+ return handleValidToken(invocation);
+ }
+ }
+
+ /**
+ * Determines what to do if an invalida token is provided. If the action implements {@link ValidationAware}
+ *
+ * @param invocation the action invocation where the invalid token failed
+ * @return the return code to indicate should be processed
+ * @throws Exception when any unexpected error occurs.
+ */
+ protected String handleInvalidToken(ActionInvocation invocation) throws Exception {
+ Object action = invocation.getAction();
+ String errorMessage = LocalizedTextUtil.findText(this.getClass(), "struts.messages.invalid.token",
+ invocation.getInvocationContext().getLocale(),
+ "The form has already been processed or no token was supplied, please try again.", new Object[0]);
+
+ if (action instanceof ValidationAware) {
+ ((ValidationAware) action).addActionError(errorMessage);
+ } else {
+ log.warn(errorMessage);
+ }
+
+ return INVALID_TOKEN_CODE;
+ }
+
+ /**
+ * Called when a valid token is found. This method invokes the action by can be changed to do something more
+ * interesting.
+ *
+ * @param invocation the action invocation
+ * @throws Exception when any unexpected error occurs.
+ */
+ protected String handleValidToken(ActionInvocation invocation) throws Exception {
+ return invocation.invoke();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/TokenSessionStoreInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/TokenSessionStoreInterceptor.java
new file mode 100644
index 000000000..faedeeb77
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/TokenSessionStoreInterceptor.java
@@ -0,0 +1,152 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.util.InvocationSessionStore;
+import org.apache.struts2.util.TokenHelper;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.Result;
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ *
+ *
+ * This interceptor builds off of the {@link TokenInterceptor}, providing advanced logic for handling invalid tokens.
+ * Unlike the normal token interceptor, this interceptor will attempt to provide intelligent fail-over in the event of
+ * multiple requests using the same session. That is, it will block subsequent requests until the first request is
+ * complete, and then instead of returning the invalid.token code, it will attempt to display the same response
+ * that the original, valid action invocation would have displayed if no multiple requests were submitted in the first
+ * place.
+ *
+ *
+ *
+ * NOTE: As this method extends off MethodFilterInterceptor, it is capable of
+ * deciding if it is applicable only to selective methods in the action class. See
+ * MethodFilterInterceptor for more info.
+ *
+ *
+ *
+ *
Interceptor parameters:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
Extending the interceptor:
+ *
+ *
+ *
+ *
+ *
+ * There are no known extension points for this interceptor.
+ *
+ *
+ *
+ *
Example code:
+ *
+ *
+ *
+ *
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="token-session/>
+ * <interceptor-ref name="basicStack"/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ *
+ * <-- In this case, myMethod of the action class will not
+ * get checked for invalidity of token -->
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="token-session>
+ * <param name="excludeMethods">myMethod</param>
+ * </interceptor-ref name="token-session>
+ * <interceptor-ref name="basicStack"/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ *
+ *
+ *
+ *
+ */
+public class TokenSessionStoreInterceptor extends TokenInterceptor {
+
+ private static final long serialVersionUID = -9032347965469098195L;
+
+ /* (non-Javadoc)
+ * @see org.apache.struts2.interceptor.TokenInterceptor#handleInvalidToken(com.opensymphony.xwork2.ActionInvocation)
+ */
+ protected String handleInvalidToken(ActionInvocation invocation) throws Exception {
+ ActionContext ac = invocation.getInvocationContext();
+
+ HttpServletRequest request = (HttpServletRequest) ac.get(ServletActionContext.HTTP_REQUEST);
+ String tokenName = TokenHelper.getTokenName();
+ String token = TokenHelper.getToken(tokenName);
+
+ Map params = ac.getParameters();
+ params.remove(tokenName);
+ params.remove(TokenHelper.TOKEN_NAME_FIELD);
+
+ if ((tokenName != null) && (token != null)) {
+ ActionInvocation savedInvocation = InvocationSessionStore.loadInvocation(tokenName, token);
+
+ if (savedInvocation != null) {
+ // set the valuestack to the request scope
+ ValueStack stack = savedInvocation.getStack();
+ request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
+
+ Result result = savedInvocation.getResult();
+
+ if ((result != null) && (savedInvocation.getProxy().getExecuteResult())) {
+ result.execute(savedInvocation);
+ }
+
+ // turn off execution of this invocations result
+ invocation.getProxy().setExecuteResult(false);
+
+ return savedInvocation.getResultCode();
+ }
+ }
+
+ return INVALID_TOKEN_CODE;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.struts2.interceptor.TokenInterceptor#handleValidToken(com.opensymphony.xwork2.ActionInvocation)
+ */
+ protected String handleValidToken(ActionInvocation invocation) throws Exception {
+ // we know the token name and token must be there
+ String key = TokenHelper.getTokenName();
+ String token = TokenHelper.getToken(key);
+ InvocationSessionStore.storeInvocation(key, token, invocation);
+
+ return invocation.invoke();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/debugging/DebuggingInterceptor.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/debugging/DebuggingInterceptor.java
new file mode 100644
index 000000000..66535199e
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/debugging/DebuggingInterceptor.java
@@ -0,0 +1,358 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.interceptor.debugging;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.views.freemarker.FreemarkerResult;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.Interceptor;
+import com.opensymphony.xwork2.interceptor.PreResultListener;
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * Provides several different debugging screens to provide insight into the
+ * data behind the page. The value of the 'debug' request parameter determines
+ * the screen:
+ *
+ * xml - Dumps the parameters, context, session, and value
+ * stack as an XML document.
+ * console - Shows a popup 'OGNL Console' that allows the
+ * user to test OGNL expressions against the value stack. The XML data from
+ * the 'xml' mode is inserted at the top of the page.
+ * command - Tests an OGNL expression and returns the
+ * string result. Only used by the OGNL console.
+ *
+ *
+ *
+ * This interceptor only is activated when devMode is enabled in
+ * struts.properties. The 'debug' parameter is removed from the parameter list
+ * before the action is executed. All operations occur before the natural
+ * Result has a chance to execute.
+ */
+public class DebuggingInterceptor implements Interceptor {
+
+ private static final long serialVersionUID = -3097324155953078783L;
+
+ private final static Log log = LogFactory.getLog(DebuggingInterceptor.class);
+
+ private String[] ignorePrefixes = new String[]{"org.apache.struts.",
+ "com.opensymphony.xwork2.", "xwork."};
+ private String[] _ignoreKeys = new String[]{"application", "session",
+ "parameters", "request"};
+ private HashSet ignoreKeys = new HashSet(Arrays.asList(_ignoreKeys));
+
+ private final static String XML_MODE = "xml";
+ private final static String CONSOLE_MODE = "console";
+ private final static String COMMAND_MODE = "command";
+
+ private final static String SESSION_KEY = "org.apache.struts2.interceptor.debugging.VALUE_STACK";
+
+ private final static String DEBUG_PARAM = "debug";
+ private final static String EXPRESSION_PARAM = "expression";
+
+ private boolean enableXmlWithConsole = false;
+
+
+ /**
+ * Unused.
+ */
+ public void init() {
+ }
+
+
+ /**
+ * Unused.
+ */
+ public void destroy() {
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.opensymphony.xwork2.interceptor.Interceptor#invoke(com.opensymphony.xwork2.ActionInvocation)
+ */
+ public String intercept(ActionInvocation inv) throws Exception {
+
+ Boolean devMode = (Boolean) ActionContext.getContext().get(
+ ActionContext.DEV_MODE);
+ boolean cont = true;
+ if (devMode) {
+ final ActionContext ctx = ActionContext.getContext();
+ String type = getParameter(DEBUG_PARAM);
+ ctx.getParameters().remove(DEBUG_PARAM);
+ if (XML_MODE.equals(type)) {
+ inv.addPreResultListener(
+ new PreResultListener() {
+ public void beforeResult(ActionInvocation inv, String result) {
+ printContext();
+ }
+ });
+ } else if (CONSOLE_MODE.equals(type)) {
+ inv.addPreResultListener(
+ new PreResultListener() {
+ public void beforeResult(ActionInvocation inv, String actionResult) {
+ String xml = "";
+ if (enableXmlWithConsole) {
+ StringWriter writer = new StringWriter();
+ printContext(new PrettyPrintWriter(writer));
+ xml = writer.toString();
+ xml = xml.replaceAll("&", "&");
+ xml = xml.replaceAll(">", ">");
+ xml = xml.replaceAll("<", "<");
+ }
+ ActionContext.getContext().put("debugXML", xml);
+
+ FreemarkerResult result = new FreemarkerResult();
+ result.setContentType("text/html");
+ result.setLocation("/org/apache/struts2/interceptor/debugging/console.ftl");
+ result.setParse(false);
+ try {
+ result.execute(inv);
+ } catch (Exception ex) {
+ log.error("Unable to create debugging console", ex);
+ }
+
+ }
+ });
+ } else if (COMMAND_MODE.equals(type)) {
+ ValueStack stack = (ValueStack) ctx.getSession().get(SESSION_KEY);
+ String cmd = getParameter(EXPRESSION_PARAM);
+
+ HttpServletResponse res = ServletActionContext.getResponse();
+ res.setContentType("text/plain");
+
+ try {
+ PrintWriter writer =
+ ServletActionContext.getResponse().getWriter();
+ writer.print(stack.findValue(cmd));
+ writer.close();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ cont = false;
+ }
+ }
+ if (cont) {
+ try {
+ return inv.invoke();
+ } finally {
+ if (devMode) {
+ final ActionContext ctx = ActionContext.getContext();
+ ctx.getSession().put(SESSION_KEY, ctx.get(ActionContext.VALUE_STACK));
+ }
+ }
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * Gets a single string from the request parameters
+ *
+ * @param key The key
+ * @return The parameter value
+ */
+ private String getParameter(String key) {
+ String[] arr = (String[]) ActionContext.getContext().getParameters().get(key);
+ if (arr != null && arr.length > 0) {
+ return arr[0];
+ }
+ return null;
+ }
+
+
+ /**
+ * Prints the current context to the response in XML format.
+ */
+ protected void printContext() {
+ HttpServletResponse res = ServletActionContext.getResponse();
+ res.setContentType("text/xml");
+
+ try {
+ PrettyPrintWriter writer = new PrettyPrintWriter(
+ ServletActionContext.getResponse().getWriter());
+ printContext(writer);
+ writer.close();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+
+ /**
+ * Prints the current request to the existing writer.
+ *
+ * @param writer The XML writer
+ */
+ protected void printContext(PrettyPrintWriter writer) {
+ ActionContext ctx = ActionContext.getContext();
+ writer.startNode(DEBUG_PARAM);
+ serializeIt(ctx.getParameters(), "parameters", writer,
+ new ArrayList());
+ writer.startNode("context");
+ String key;
+ Map ctxMap = ctx.getContextMap();
+ for (Object o : ctxMap.keySet()) {
+ key = o.toString();
+ boolean print = !ignoreKeys.contains(key);
+
+ for (String ignorePrefixe : ignorePrefixes) {
+ if (key.startsWith(ignorePrefixe)) {
+ print = false;
+ break;
+ }
+ }
+ if (print) {
+ serializeIt(ctxMap.get(key), key, writer, new ArrayList());
+ }
+ }
+ writer.endNode();
+ serializeIt(ctx.getSession(), "request", writer, new ArrayList());
+ serializeIt(ctx.getSession(), "session", writer, new ArrayList());
+
+ ValueStack stack = (ValueStack) ctx.get(ActionContext.VALUE_STACK);
+ serializeIt(stack.getRoot(), "valueStack", writer, new ArrayList());
+ writer.endNode();
+ }
+
+
+ /**
+ * Recursive function to serialize objects to XML. Currently it will
+ * serialize Collections, maps, Arrays, and JavaBeans. It maintains a stack
+ * of objects serialized already in the current functioncall. This is used
+ * to avoid looping (stack overflow) of circular linked objects. Struts and
+ * XWork objects are ignored.
+ *
+ * @param bean The object you want serialized.
+ * @param name The name of the object, used for element <name/>
+ * @param writer The XML writer
+ * @param stack List of objects we're serializing since the first calling
+ * of this function (to prevent looping on circular references).
+ */
+ protected void serializeIt(Object bean, String name,
+ PrettyPrintWriter writer, List stack) {
+ writer.flush();
+ // Check stack for this object
+ if ((bean != null) && (stack.contains(bean))) {
+ if (log.isInfoEnabled()) {
+ log.info("Circular reference detected, not serializing object: "
+ + name);
+ }
+ return;
+ } else if (bean != null) {
+ // Push object onto stack.
+ // Don't push null objects ( handled below)
+ stack.add(bean);
+ }
+ if (bean == null) {
+ return;
+ }
+ String clsName = bean.getClass().getName();
+
+ writer.startNode(name);
+
+ // It depends on the object and it's value what todo next:
+ if (bean instanceof Collection) {
+ Collection col = (Collection) bean;
+
+ // Iterate through components, and call ourselves to process
+ // elements
+ for (Object aCol : col) {
+ serializeIt(aCol, "value", writer, stack);
+ }
+ } else if (bean instanceof Map) {
+
+ Map map = (Map) bean;
+
+ // Loop through keys and call ourselves
+ for (Object key : map.keySet()) {
+ Object Objvalue = map.get(key);
+ serializeIt(Objvalue, key.toString(), writer, stack);
+ }
+ } else if (bean.getClass().isArray()) {
+ // It's an array, loop through it and keep calling ourselves
+ for (int i = 0; i < Array.getLength(bean); i++) {
+ serializeIt(Array.get(bean, i), "arrayitem", writer, stack);
+ }
+ } else {
+ if (clsName.startsWith("java.lang")) {
+ writer.setValue(bean.toString());
+ } else {
+ // Not java.lang, so we can call ourselves with this object's
+ // values
+ try {
+ BeanInfo info = Introspector.getBeanInfo(bean.getClass());
+ PropertyDescriptor[] props = info.getPropertyDescriptors();
+
+ for (PropertyDescriptor prop : props) {
+ String n = prop.getName();
+ Method m = prop.getReadMethod();
+
+ // Call ourselves with the result of the method
+ // invocation
+ if (m != null) {
+ serializeIt(m.invoke(bean), n, writer, stack);
+ }
+ }
+ } catch (Exception e) {
+ log.error(e, e);
+ }
+ }
+ }
+
+ writer.endNode();
+
+ // Remove object from stack
+ stack.remove(bean);
+ }
+
+
+ /**
+ * @param enableXmlWithConsole the enableXmlWithConsole to set
+ */
+ public void setEnableXmlWithConsole(boolean enableXmlWithConsole) {
+ this.enableXmlWithConsole = enableXmlWithConsole;
+ }
+
+
+
+}
+
diff --git a/trunk/core/src/main/java/org/apache/struts2/interceptor/debugging/PrettyPrintWriter.java b/trunk/core/src/main/java/org/apache/struts2/interceptor/debugging/PrettyPrintWriter.java
new file mode 100644
index 000000000..f73681424
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/interceptor/debugging/PrettyPrintWriter.java
@@ -0,0 +1,174 @@
+package org.apache.struts2.interceptor.debugging;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Stack;
+
+/**
+ * A simple writer that outputs XML in a pretty-printed indented stream.
+ *
+ * By default, the chars & < > " ' \r are escaped and replaced with a suitable XML entity.
+ * To alter this behavior, override the the {@link #writeText(com.thoughtworks.xstream.core.util.QuickWriter, String)}
+ * and {@link #writeAttributeValue(com.thoughtworks.xstream.core.util.QuickWriter, String)} methods.
+ *
+ * This code was taken from the XStream project under the BSD license.
+ *
+ */
+public class PrettyPrintWriter {
+
+ private final PrintWriter writer;
+ private final Stack elementStack = new Stack();
+ private final char[] lineIndenter;
+
+ private boolean tagInProgress;
+ private int depth;
+ private boolean readyForNewLine;
+ private boolean tagIsEmpty;
+ private String newLine;
+
+ private static final char[] NULL = "".toCharArray();
+ private static final char[] AMP = "&".toCharArray();
+ private static final char[] LT = "<".toCharArray();
+ private static final char[] GT = ">".toCharArray();
+ private static final char[] SLASH_R = "
".toCharArray();
+ private static final char[] QUOT = """.toCharArray();
+ private static final char[] APOS = "'".toCharArray();
+ private static final char[] CLOSE = "".toCharArray();
+
+ public PrettyPrintWriter(Writer writer, char[] lineIndenter, String newLine) {
+ this.writer = new PrintWriter(writer);
+ this.lineIndenter = lineIndenter;
+ this.newLine = newLine;
+ }
+
+ public PrettyPrintWriter(Writer writer, char[] lineIndenter) {
+ this(writer, lineIndenter, "\n");
+ }
+
+ public PrettyPrintWriter(Writer writer, String lineIndenter, String newLine) {
+ this(writer, lineIndenter.toCharArray(), newLine);
+ }
+
+ public PrettyPrintWriter(Writer writer, String lineIndenter) {
+ this(writer, lineIndenter.toCharArray());
+ }
+
+ public PrettyPrintWriter(Writer writer) {
+ this(writer, new char[]{' ', ' '});
+ }
+
+ public void startNode(String name) {
+ tagIsEmpty = false;
+ finishTag();
+ writer.write('<');
+ writer.write(name);
+ elementStack.push(name);
+ tagInProgress = true;
+ depth++;
+ readyForNewLine = true;
+ tagIsEmpty = true;
+ }
+
+ public void setValue(String text) {
+ readyForNewLine = false;
+ tagIsEmpty = false;
+ finishTag();
+
+ writeText(writer, text);
+ }
+
+ public void addAttribute(String key, String value) {
+ writer.write(' ');
+ writer.write(key);
+ writer.write('=');
+ writer.write('\"');
+ writeAttributeValue(writer, value);
+ writer.write('\"');
+ }
+
+ protected void writeAttributeValue(PrintWriter writer, String text) {
+ writeText(text);
+ }
+
+ protected void writeText(PrintWriter writer, String text) {
+ writeText(text);
+ }
+
+ private void writeText(String text) {
+ int length = text.length();
+ for (int i = 0; i < length; i++) {
+ char c = text.charAt(i);
+ switch (c) {
+ case '\0':
+ this.writer.write(NULL);
+ break;
+ case '&':
+ this.writer.write(AMP);
+ break;
+ case '<':
+ this.writer.write(LT);
+ break;
+ case '>':
+ this.writer.write(GT);
+ break;
+ case '"':
+ this.writer.write(QUOT);
+ break;
+ case '\'':
+ this.writer.write(APOS);
+ break;
+ case '\r':
+ this.writer.write(SLASH_R);
+ break;
+ default:
+ this.writer.write(c);
+ }
+ }
+ }
+
+ public void endNode() {
+ depth--;
+ if (tagIsEmpty) {
+ writer.write('/');
+ readyForNewLine = false;
+ finishTag();
+ elementStack.pop();
+ } else {
+ finishTag();
+ writer.write(CLOSE);
+ writer.write((String)elementStack.pop());
+ writer.write('>');
+ }
+ readyForNewLine = true;
+ if (depth == 0 ) {
+ writer.flush();
+ }
+ }
+
+ private void finishTag() {
+ if (tagInProgress) {
+ writer.write('>');
+ }
+ tagInProgress = false;
+ if (readyForNewLine) {
+ endOfLine();
+ }
+ readyForNewLine = false;
+ tagIsEmpty = false;
+ }
+
+ protected void endOfLine() {
+ writer.write(newLine);
+ for (int i = 0; i < depth; i++) {
+ writer.write(lineIndenter);
+ }
+ }
+
+ public void flush() {
+ writer.flush();
+ }
+
+ public void close() {
+ writer.close();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/PortletActionConstants.java b/trunk/core/src/main/java/org/apache/struts2/portlet/PortletActionConstants.java
new file mode 100644
index 000000000..2d452a2cb
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/PortletActionConstants.java
@@ -0,0 +1,103 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet;
+
+/**
+ * Interface defining some constants used in the Struts portlet implementation
+ *
+ */
+public interface PortletActionConstants {
+ /**
+ * Default action name to use when no default action has been configured in the portlet
+ * init parameters.
+ */
+ String DEFAULT_ACTION_NAME = "default";
+
+ /**
+ * Action name parameter name
+ */
+ String ACTION_PARAM = "struts.portlet.action";
+
+ /**
+ * Key for parameter holding the last executed portlet mode.
+ */
+ String MODE_PARAM = "struts.portlet.mode";
+
+ /**
+ * Key used for looking up and storing the portlet phase
+ */
+ String PHASE = "struts.portlet.phase";
+
+ /**
+ * Constant used for the render phase (
+ * {@link javax.portlet.Portlet#render(javax.portlet.RenderRequest, javax.portlet.RenderResponse)})
+ */
+ Integer RENDER_PHASE = new Integer(1);
+
+ /**
+ * Constant used for the event phase (
+ * {@link javax.portlet.Portlet#processAction(javax.portlet.ActionRequest, javax.portlet.ActionResponse)})
+ */
+ Integer EVENT_PHASE = new Integer(2);
+
+ /**
+ * Key used for looking up and storing the
+ * {@link javax.portlet.PortletRequest}
+ */
+ String REQUEST = "struts.portlet.request";
+
+ /**
+ * Key used for looking up and storing the
+ * {@link javax.portlet.PortletResponse}
+ */
+ String RESPONSE = "struts.portlet.response";
+
+ /**
+ * Key used for looking up and storing the action that was invoked in the event phase.
+ */
+ String EVENT_ACTION = "struts.portlet.eventAction";
+
+ /**
+ * Key used for looking up and storing the
+ * {@link javax.portlet.PortletConfig}
+ */
+ String PORTLET_CONFIG = "struts.portlet.config";
+
+ /**
+ * Name of the action used as error handler
+ */
+ String ERROR_ACTION = "errorHandler";
+
+ /**
+ * Key for the portlet namespace stored in the
+ * {@link org.apache.struts2.portlet.context.PortletActionContext}.
+ */
+ String PORTLET_NAMESPACE = "struts.portlet.portletNamespace";
+
+ /**
+ * Key for the mode-to-namespace map stored in the
+ * {@link org.apache.struts2.portlet.context.PortletActionContext}.
+ */
+ String MODE_NAMESPACE_MAP = "struts.portlet.modeNamespaceMap";
+
+ /**
+ * Key for the default action name for the portlet, stored in the
+ * {@link org.apache.struts2.portlet.context.PortletActionContext}.
+ */
+ String DEFAULT_ACTION_FOR_MODE = "struts.portlet.defaultActionForMode";
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/PortletApplicationMap.java b/trunk/core/src/main/java/org/apache/struts2/portlet/PortletApplicationMap.java
new file mode 100644
index 000000000..34c5223c4
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/PortletApplicationMap.java
@@ -0,0 +1,203 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet;
+
+import java.io.Serializable;
+import java.util.AbstractMap;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.portlet.PortletContext;
+
+/**
+ * Portlet specific {@link java.util.Map} implementation representing the
+ * {@link javax.portlet.PortletContext} of a Portlet.
+ *
+ */
+public class PortletApplicationMap extends AbstractMap implements Serializable {
+
+ private static final long serialVersionUID = 2296107511063504414L;
+
+ private PortletContext context;
+
+ private Set entries;
+
+ /**
+ * Creates a new map object given the {@link PortletContext}.
+ *
+ * @param ctx The portlet context.
+ */
+ public PortletApplicationMap(PortletContext ctx) {
+ this.context = ctx;
+ }
+
+ /**
+ * Removes all entries from the Map and removes all attributes from the
+ * portlet context.
+ */
+ public void clear() {
+ entries = null;
+
+ Enumeration e = context.getAttributeNames();
+
+ while (e.hasMoreElements()) {
+ context.removeAttribute(e.nextElement().toString());
+ }
+ }
+
+ /**
+ * Creates a Set of all portlet context attributes as well as context init
+ * parameters.
+ *
+ * @return a Set of all portlet context attributes as well as context init
+ * parameters.
+ */
+ public Set entrySet() {
+ if (entries == null) {
+ entries = new HashSet();
+
+ // Add portlet context attributes
+ Enumeration enumeration = context.getAttributeNames();
+
+ while (enumeration.hasMoreElements()) {
+ final String key = enumeration.nextElement().toString();
+ final Object value = context.getAttribute(key);
+ entries.add(new Map.Entry() {
+ public boolean equals(Object obj) {
+ Map.Entry entry = (Map.Entry) obj;
+
+ return ((key == null) ? (entry.getKey() == null) : key
+ .equals(entry.getKey()))
+ && ((value == null) ? (entry.getValue() == null)
+ : value.equals(entry.getValue()));
+ }
+
+ public int hashCode() {
+ return ((key == null) ? 0 : key.hashCode())
+ ^ ((value == null) ? 0 : value.hashCode());
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Object setValue(Object obj) {
+ context.setAttribute(key.toString(), obj);
+
+ return value;
+ }
+ });
+ }
+
+ // Add portlet context init params
+ enumeration = context.getInitParameterNames();
+
+ while (enumeration.hasMoreElements()) {
+ final String key = enumeration.nextElement().toString();
+ final Object value = context.getInitParameter(key);
+ entries.add(new Map.Entry() {
+ public boolean equals(Object obj) {
+ Map.Entry entry = (Map.Entry) obj;
+
+ return ((key == null) ? (entry.getKey() == null) : key
+ .equals(entry.getKey()))
+ && ((value == null) ? (entry.getValue() == null)
+ : value.equals(entry.getValue()));
+ }
+
+ public int hashCode() {
+ return ((key == null) ? 0 : key.hashCode())
+ ^ ((value == null) ? 0 : value.hashCode());
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Object setValue(Object obj) {
+ context.setAttribute(key.toString(), obj);
+
+ return value;
+ }
+ });
+ }
+ }
+
+ return entries;
+ }
+
+ /**
+ * Returns the portlet context attribute or init parameter based on the
+ * given key. If the entry is not found, null is returned.
+ *
+ * @param key
+ * the entry key.
+ * @return the portlet context attribute or init parameter or null
+ * if the entry is not found.
+ */
+ public Object get(Object key) {
+ // Try context attributes first, then init params
+ // This gives the proper shadowing effects
+ String keyString = key.toString();
+ Object value = context.getAttribute(keyString);
+
+ return (value == null) ? context.getInitParameter(keyString) : value;
+ }
+
+ /**
+ * Sets a portlet context attribute given a attribute name and value.
+ *
+ * @param key
+ * the name of the attribute.
+ * @param value
+ * the value to set.
+ * @return the attribute that was just set.
+ */
+ public Object put(Object key, Object value) {
+ entries = null;
+ context.setAttribute(key.toString(), value);
+
+ return get(key);
+ }
+
+ /**
+ * Removes the specified portlet context attribute.
+ *
+ * @param key
+ * the attribute to remove.
+ * @return the entry that was just removed.
+ */
+ public Object remove(Object key) {
+ entries = null;
+
+ Object value = get(key);
+ context.removeAttribute(key.toString());
+
+ return value;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/PortletRequestMap.java b/trunk/core/src/main/java/org/apache/struts2/portlet/PortletRequestMap.java
new file mode 100644
index 000000000..b76b5baa3
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/PortletRequestMap.java
@@ -0,0 +1,164 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet;
+
+import java.util.AbstractMap;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.portlet.PortletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A simple implementation of the {@link java.util.Map} interface to handle a collection of request attributes.
+ *
+ */
+public class PortletRequestMap extends AbstractMap {
+
+ private static final Log LOG = LogFactory.getLog(PortletRequestMap.class);
+
+ private Set entries = null;
+ private PortletRequest request = null;
+
+ /**
+ * Saves the request to use as the backing for getting and setting values
+ *
+ * @param request the portlet request.
+ */
+ public PortletRequestMap(PortletRequest request) {
+ this.request = request;
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Dumping request parameters: ");
+ Iterator params = request.getParameterMap().keySet().iterator();
+ while(params.hasNext()) {
+ String key = (String)params.next();
+ String val = request.getParameter(key);
+ LOG.debug(key + " = " + val);
+ }
+ }
+ }
+
+ /**
+ * Removes all attributes from the request as well as clears entries in this
+ * map.
+ */
+ public void clear() {
+ entries = null;
+ Enumeration keys = request.getAttributeNames();
+
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ request.removeAttribute(key);
+ }
+ }
+
+ /**
+ * Returns a Set of attributes from the portlet request.
+ *
+ * @return a Set of attributes from the portlet request.
+ */
+ public Set entrySet() {
+ if (entries == null) {
+ entries = new HashSet();
+
+ Enumeration enumeration = request.getAttributeNames();
+
+ while (enumeration.hasMoreElements()) {
+ final String key = enumeration.nextElement().toString();
+ final Object value = request.getAttribute(key);
+ entries.add(new Entry() {
+ public boolean equals(Object obj) {
+ Entry entry = (Entry) obj;
+
+ return ((key == null) ? (entry.getKey() == null) : key
+ .equals(entry.getKey()))
+ && ((value == null) ? (entry.getValue() == null)
+ : value.equals(entry.getValue()));
+ }
+
+ public int hashCode() {
+ return ((key == null) ? 0 : key.hashCode())
+ ^ ((value == null) ? 0 : value.hashCode());
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Object setValue(Object obj) {
+ request.setAttribute(key, obj);
+
+ return value;
+ }
+ });
+ }
+ }
+
+ return entries;
+ }
+
+ /**
+ * Returns the request attribute associated with the given key or
+ * null if it doesn't exist.
+ *
+ * @param key the name of the request attribute.
+ * @return the request attribute or null if it doesn't exist.
+ */
+ public Object get(Object key) {
+ return request.getAttribute(key.toString());
+ }
+
+ /**
+ * Saves an attribute in the request.
+ *
+ * @param key the name of the request attribute.
+ * @param value the value to set.
+ * @return the object that was just set.
+ */
+ public Object put(Object key, Object value) {
+ entries = null;
+ request.setAttribute(key.toString(), value);
+
+ return get(key);
+ }
+
+ /**
+ * Removes the specified request attribute.
+ *
+ * @param key the name of the attribute to remove.
+ * @return the value that was removed or null if the value was
+ * not found (and hence, not removed).
+ */
+ public Object remove(Object key) {
+ entries = null;
+
+ Object value = get(key);
+ request.removeAttribute(key.toString());
+
+ return value;
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/PortletSessionMap.java b/trunk/core/src/main/java/org/apache/struts2/portlet/PortletSessionMap.java
new file mode 100644
index 000000000..b3ff045f6
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/PortletSessionMap.java
@@ -0,0 +1,168 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet;
+
+import java.util.AbstractMap;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletSession;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A simple implementation of the {@link java.util.Map} interface to handle a collection of portlet session
+ * attributes. The {@link #entrySet()} method enumerates over all session attributes and creates a Set of entries.
+ * Note, this will occur lazily - only when the entry set is asked for.
+ *
+ */
+public class PortletSessionMap extends AbstractMap {
+
+ private static final Log LOG = LogFactory.getLog(PortletSessionMap.class);
+
+ private PortletSession session = null;
+ private Set entries = null;
+
+ /**
+ * Creates a new session map given a portlet request.
+ *
+ * @param request the portlet request object.
+ */
+ public PortletSessionMap(PortletRequest request) {
+ this.session = request.getPortletSession();
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Dumping session info: ");
+ Enumeration enumeration = session.getAttributeNames();
+ while(enumeration.hasMoreElements()) {
+ String key = (String)enumeration.nextElement();
+ Object val = session.getAttribute(key);
+ LOG.debug(key + " = " + val);
+ }
+ }
+ }
+
+ /**
+ * @see java.util.Map#entrySet()
+ */
+ public Set entrySet() {
+ synchronized (session) {
+ if (entries == null) {
+ entries = new HashSet();
+
+ Enumeration enumeration = session.getAttributeNames();
+
+ while (enumeration.hasMoreElements()) {
+ final String key = enumeration.nextElement().toString();
+ final Object value = session.getAttribute(key);
+ entries.add(new Map.Entry() {
+ public boolean equals(Object obj) {
+ Map.Entry entry = (Map.Entry) obj;
+
+ return ((key == null) ? (entry.getKey() == null)
+ : key.equals(entry.getKey()))
+ && ((value == null) ? (entry.getValue() == null)
+ : value.equals(entry.getValue()));
+ }
+
+ public int hashCode() {
+ return ((key == null) ? 0 : key.hashCode())
+ ^ ((value == null) ? 0 : value.hashCode());
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Object setValue(Object obj) {
+ session.setAttribute(key, obj);
+
+ return value;
+ }
+ });
+ }
+ }
+ }
+
+ return entries;
+ }
+
+ /**
+ * Returns the session attribute associated with the given key or
+ * null if it doesn't exist.
+ *
+ * @param key the name of the session attribute.
+ * @return the session attribute or null if it doesn't exist.
+ */
+ public Object get(Object key) {
+ synchronized (session) {
+ return session.getAttribute(key.toString());
+ }
+ }
+
+ /**
+ * Saves an attribute in the session.
+ *
+ * @param key the name of the session attribute.
+ * @param value the value to set.
+ * @return the object that was just set.
+ */
+ public Object put(Object key, Object value) {
+ synchronized (session) {
+ entries = null;
+ session.setAttribute(key.toString(), value);
+
+ return get(key);
+ }
+ }
+
+ /**
+ * @see java.util.Map#clear()
+ */
+ public void clear() {
+ synchronized (session) {
+ entries = null;
+ session.invalidate();
+ }
+ }
+
+ /**
+ * Removes the specified session attribute.
+ *
+ * @param key the name of the attribute to remove.
+ * @return the value that was removed or null if the value was
+ * not found (and hence, not removed).
+ */
+ public Object remove(Object key) {
+ synchronized (session) {
+ entries = null;
+
+ Object value = get(key);
+ session.removeAttribute(key.toString());
+
+ return value;
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/context/PortletActionContext.java b/trunk/core/src/main/java/org/apache/struts2/portlet/context/PortletActionContext.java
new file mode 100644
index 000000000..86870b409
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/context/PortletActionContext.java
@@ -0,0 +1,193 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet.context;
+
+import java.util.Map;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletResponse;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+
+import org.apache.struts2.portlet.PortletActionConstants;
+
+import com.opensymphony.xwork2.ActionContext;
+
+
+/**
+ * PortletActionContext. ActionContext thread local for the portlet environment.
+ *
+ * @version $Revision$ $Date$
+ */
+public class PortletActionContext implements PortletActionConstants {
+
+ /**
+ * Get the PortletConfig of the portlet that is executing.
+ *
+ * @return The PortletConfig of the executing portlet.
+ */
+ public static PortletConfig getPortletConfig() {
+ return (PortletConfig) getContext().get(PORTLET_CONFIG);
+ }
+
+ /**
+ * Get the RenderRequest. Can only be invoked in the render phase.
+ *
+ * @return The current RenderRequest.
+ * @throws IllegalStateException If the method is invoked in the wrong phase.
+ */
+ public static RenderRequest getRenderRequest() {
+ if (!isRender()) {
+ throw new IllegalStateException(
+ "RenderRequest cannot be obtained in event phase");
+ }
+ return (RenderRequest) getContext().get(REQUEST);
+ }
+
+ /**
+ * Get the RenderResponse. Can only be invoked in the render phase.
+ *
+ * @return The current RenderResponse.
+ * @throws IllegalStateException If the method is invoked in the wrong phase.
+ */
+ public static RenderResponse getRenderResponse() {
+ if (!isRender()) {
+ throw new IllegalStateException(
+ "RenderResponse cannot be obtained in event phase");
+ }
+ return (RenderResponse) getContext().get(RESPONSE);
+ }
+
+ /**
+ * Get the ActionRequest. Can only be invoked in the event phase.
+ *
+ * @return The current ActionRequest.
+ * @throws IllegalStateException If the method is invoked in the wrong phase.
+ */
+ public static ActionRequest getActionRequest() {
+ if (!isEvent()) {
+ throw new IllegalStateException(
+ "ActionRequest cannot be obtained in render phase");
+ }
+ return (ActionRequest) getContext().get(REQUEST);
+ }
+
+ /**
+ * Get the ActionRequest. Can only be invoked in the event phase.
+ *
+ * @return The current ActionRequest.
+ * @throws IllegalStateException If the method is invoked in the wrong phase.
+ */
+ public static ActionResponse getActionResponse() {
+ if (!isEvent()) {
+ throw new IllegalStateException(
+ "ActionResponse cannot be obtained in render phase");
+ }
+ return (ActionResponse) getContext().get(RESPONSE);
+ }
+
+ /**
+ * Get the action namespace of the portlet. Used to organize actions for multiple portlets in
+ * the same portlet application.
+ *
+ * @return The portlet namespace as defined in portlet.xml and struts.xml
+ */
+ public static String getPortletNamespace() {
+ return (String)getContext().get(PORTLET_NAMESPACE);
+ }
+
+ /**
+ * Get the current PortletRequest.
+ *
+ * @return The current PortletRequest.
+ */
+ public static PortletRequest getRequest() {
+ return (PortletRequest) getContext().get(REQUEST);
+ }
+
+ /**
+ * Get the current PortletResponse
+ *
+ * @return The current PortletResponse.
+ */
+ public static PortletResponse getResponse() {
+ return (PortletResponse) getContext().get(RESPONSE);
+ }
+
+ /**
+ * Get the phase that the portlet is executing in.
+ *
+ * @return {@link PortletActionConstants#RENDER_PHASE} in render phase, and
+ * {@link PortletActionConstants#EVENT_PHASE} in the event phase.
+ */
+ public static Integer getPhase() {
+ return (Integer) getContext().get(PHASE);
+ }
+
+ /**
+ * @return true if the Portlet is executing in render phase.
+ */
+ public static boolean isRender() {
+ return PortletActionConstants.RENDER_PHASE.equals(getPhase());
+ }
+
+ /**
+ * @return true if the Portlet is executing in the event phase.
+ */
+ public static boolean isEvent() {
+ return PortletActionConstants.EVENT_PHASE.equals(getPhase());
+ }
+
+ /**
+ * @return The current ActionContext.
+ */
+ private static ActionContext getContext() {
+ return ActionContext.getContext();
+ }
+
+ /**
+ * Check to see if the current request is a portlet request.
+ *
+ * @return true if the current request is a portlet request.
+ */
+ public static boolean isPortletRequest() {
+ return getRequest() != null;
+ }
+
+ /**
+ * Get the default action name for the current mode.
+ *
+ * @return The default action name for the current portlet mode.
+ */
+ public static String getDefaultActionForMode() {
+ return (String)getContext().get(DEFAULT_ACTION_FOR_MODE);
+ }
+
+ /**
+ * Get the namespace to mode mappings.
+ *
+ * @return The map of the namespaces for each mode.
+ */
+ public static Map getModeNamespaceMap() {
+ return (Map)getContext().get(MODE_NAMESPACE_MAP);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/context/PreparatorServlet.java b/trunk/core/src/main/java/org/apache/struts2/portlet/context/PreparatorServlet.java
new file mode 100644
index 000000000..181229de0
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/context/PreparatorServlet.java
@@ -0,0 +1,63 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet.context;
+
+import java.io.IOException;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.StrutsStatics;
+
+import com.opensymphony.xwork2.ActionContext;
+
+/**
+ * Since a portlet is not dispatched the same way as a servlet, the
+ * {@link org.apache.struts2.ServletActionContext} is not immediately available, as it
+ * depends on objects from the servlet API. However, the WW2 view implementations require access
+ * to the objects in the {@link org.apache.struts2.ServletActionContext}, and this servlet
+ * makes sure that these are available when the portlet actions are executing the render results.
+ *
+ */
+public class PreparatorServlet extends HttpServlet implements StrutsStatics {
+
+ private static final long serialVersionUID = 1853399729352984089L;
+
+ private final static Log LOG = LogFactory.getLog(PreparatorServlet.class);
+
+ /**
+ * Prepares the {@link org.apache.struts2.ServletActionContext} with the
+ * {@link ServletContext}, {@link HttpServletRequest} and {@link HttpServletResponse}.
+ */
+ public void service(HttpServletRequest servletRequest,
+ HttpServletResponse servletResponse) throws ServletException,
+ IOException {
+ LOG.debug("Preparing servlet objects for dispatch");
+ ServletContext ctx = getServletContext();
+ ActionContext.getContext().put(SERVLET_CONTEXT, ctx);
+ ActionContext.getContext().put(HTTP_REQUEST, servletRequest);
+ ActionContext.getContext().put(HTTP_RESPONSE, servletResponse);
+ LOG.debug("Preparation complete");
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/context/ServletContextHolderListener.java b/trunk/core/src/main/java/org/apache/struts2/portlet/context/ServletContextHolderListener.java
new file mode 100644
index 000000000..23d174b52
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/context/ServletContextHolderListener.java
@@ -0,0 +1,60 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet.context;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+/**
+ * Some of the factory/managers (e.g. the ObjectFactory) need access to
+ * the {@link org.apache.struts2.ServletActionContext} object when initializing.
+ * This {@link javax.servlet.ServletContextListener} keeps a reference to the
+ * {@link javax.servlet.ServletContext} and exposes it through a public static
+ * method.
+ *
+ */
+public class ServletContextHolderListener implements ServletContextListener {
+
+ private static ServletContext context = null;
+
+ /**
+ * @return The current servlet context
+ */
+ public static ServletContext getServletContext() {
+ return context;
+ }
+
+ /**
+ * Stores the reference to the {@link ServletContext}.
+ *
+ * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
+ */
+ public void contextInitialized(ServletContextEvent event) {
+ context = event.getServletContext();
+
+ }
+
+ /**
+ * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
+ */
+ public void contextDestroyed(ServletContextEvent event) {
+ context = null;
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/dispatcher/DirectRenderFromEventAction.java b/trunk/core/src/main/java/org/apache/struts2/portlet/dispatcher/DirectRenderFromEventAction.java
new file mode 100644
index 000000000..7a9172a49
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/dispatcher/DirectRenderFromEventAction.java
@@ -0,0 +1,70 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet.dispatcher;
+
+import com.opensymphony.xwork2.Action;
+
+import java.io.Serializable;
+
+/**
+ * When a portlet is targetted for an event, the portlet will receive two
+ * portlet requests, one for the event phase, and then followed by a render
+ * operation. When in the event phase, the action that is executed can't render
+ * any output. This means that if an action in the XWork configuration is executed in the event
+ * phase, and the action is set up with a result that should render something, the result can't
+ * immediately be executed. The portlet needs to "wait" to the render phase to do the
+ * rendering.
+ *
+ * When the {@link org.apache.struts2.portlet.result.PortletResult} detects such a
+ * scenario, instead of executing the actual view, it prepares a couple of render parameters
+ * specifying this action and the location of the view, which then will be executed in the
+ * following render request.
+ */
+public class DirectRenderFromEventAction implements Action, Serializable {
+
+ private static final long serialVersionUID = -1814807772308405785L;
+
+ private String location = null;
+
+ /**
+ * Get the location of the view.
+ *
+ * @return Returns the location.
+ */
+ public String getLocation() {
+ return location;
+ }
+
+ /**
+ * Set the location of the view.
+ *
+ * @param location The location to set.
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Always return success.
+ *
+ * @return SUCCESS
+ */
+ public String execute() throws Exception {
+ return SUCCESS;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java b/trunk/core/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java
new file mode 100644
index 000000000..321bff6dc
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java
@@ -0,0 +1,597 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet.dispatcher;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
+import javax.portlet.GenericPortlet;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletException;
+import javax.portlet.PortletMode;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletResponse;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.config.Settings;
+import org.apache.struts2.dispatcher.ApplicationMap;
+import org.apache.struts2.dispatcher.Dispatcher;
+import org.apache.struts2.dispatcher.RequestMap;
+import org.apache.struts2.dispatcher.SessionMap;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.PortletApplicationMap;
+import org.apache.struts2.portlet.PortletRequestMap;
+import org.apache.struts2.portlet.PortletSessionMap;
+import org.apache.struts2.portlet.context.PortletActionContext;
+import org.apache.struts2.portlet.context.ServletContextHolderListener;
+import org.apache.struts2.util.AttributeMap;
+import org.apache.struts2.util.ObjectFactoryInitializable;
+
+import com.opensymphony.xwork2.util.ClassLoaderUtil;
+import com.opensymphony.xwork2.util.FileManager;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.ActionProxyFactory;
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.util.LocalizedTextUtil;
+
+/**
+ *
+ *
+ * Struts JSR-168 portlet dispatcher. Similar to the WW2 Servlet dispatcher,
+ * but adjusted to a portal environment. The portlet is configured through the portlet.xml
+ * descriptor. Examples and descriptions follow below:
+ *
+ *
+ *
+ * @author Nils-Helge Garli
+ * @author Rainer Hermanns
+ *
+ * Init parameters
+ *
+ *
+ *
+ * Name
+ * Description
+ * Default value
+ *
+ *
+ * portletNamespace The namespace for the portlet in the xwork configuration. This
+ * namespace is prepended to all action lookups, and makes it possible to host multiple
+ * portlets in the same portlet application. If this parameter is set, the complete namespace
+ * will be /portletNamespace/modeNamespace/actionName The default namespace
+ *
+ *
+ * viewNamespace Base namespace in the xwork configuration for the view portlet
+ * mode The default namespace
+ *
+ *
+ * editNamespace Base namespace in the xwork configuration for the edit portlet
+ * mode The default namespace
+ *
+ *
+ * helpNamespace Base namespace in the xwork configuration for the help portlet
+ * mode The default namespace
+ *
+ *
+ * defaultViewAction Default action to invoke in the view portlet mode if no action is
+ * specified default
+ *
+ *
+ * defaultEditAction Default action to invoke in the edit portlet mode if no action is
+ * specified default
+ *
+ *
+ * defaultHelpAction Default action to invoke in the help portlet mode if no action is
+ * specified default
+ *
+ *
+ *
+ * Example:
+ *
+ *
+ *
+ * <init-param>
+ * <!-- The view mode namespace. Maps to a namespace in the xwork config file -->
+ * <name>viewNamespace</name>
+ * <value>/view</value>
+ * </init-param>
+ * <init-param>
+ * <!-- The default action to invoke in view mode -->
+ * <name>defaultViewAction</name>
+ * <value>index</value>
+ * </init-param>
+ * <init-param>
+ * <!-- The view mode namespace. Maps to a namespace in the xwork config file -->
+ * <name>editNamespace</name>
+ * <value>/edit</value>
+ * </init-param>
+ * <init-param>
+ * <!-- The default action to invoke in view mode -->
+ * <name>defaultEditAction</name>
+ * <value>index</value>
+ * </init-param>
+ * <init-param>
+ * <!-- The view mode namespace. Maps to a namespace in the xwork config file -->
+ * <name>helpNamespace</name>
+ * <value>/help</value>
+ * </init-param>
+ * <init-param>
+ * <!-- The default action to invoke in view mode -->
+ * <name>defaultHelpAction</name>
+ * <value>index</value>
+ * </init-param>
+ *
+ *
+ *
+ */
+public class Jsr168Dispatcher extends GenericPortlet implements StrutsStatics,
+ PortletActionConstants {
+
+ private static final Log LOG = LogFactory.getLog(Jsr168Dispatcher.class);
+
+ private ActionProxyFactory factory = null;
+
+ private Map modeMap = new HashMap(3);
+
+ private Map actionMap = new HashMap(3);
+
+ private String portletNamespace = null;
+
+ private Dispatcher dispatcherUtils;
+
+ /**
+ * Initialize the portlet with the init parameters from portlet.xml
+ */
+ public void init(PortletConfig cfg) throws PortletException {
+ super.init(cfg);
+ LOG.debug("Initializing portlet " + getPortletName());
+ // For testability
+ if (factory == null) {
+ factory = ActionProxyFactory.getFactory();
+ }
+ portletNamespace = cfg.getInitParameter("portletNamespace");
+ LOG.debug("PortletNamespace: " + portletNamespace);
+ parseModeConfig(cfg, PortletMode.VIEW, "viewNamespace",
+ "defaultViewAction");
+ parseModeConfig(cfg, PortletMode.EDIT, "editNamespace",
+ "defaultEditAction");
+ parseModeConfig(cfg, PortletMode.HELP, "helpNamespace",
+ "defaultHelpAction");
+ parseModeConfig(cfg, new PortletMode("config"), "configNamespace",
+ "defaultConfigAction");
+ parseModeConfig(cfg, new PortletMode("about"), "aboutNamespace",
+ "defaultAboutAction");
+ parseModeConfig(cfg, new PortletMode("print"), "printNamespace",
+ "defaultPrintAction");
+ parseModeConfig(cfg, new PortletMode("preview"), "previewNamespace",
+ "defaultPreviewAction");
+ parseModeConfig(cfg, new PortletMode("edit_defaults"),
+ "editDefaultsNamespace", "defaultEditDefaultsAction");
+ if (StringUtils.isEmpty(portletNamespace)) {
+ portletNamespace = "";
+ }
+ LocalizedTextUtil
+ .addDefaultResourceBundle("org/apache/struts2/struts-messages");
+
+ //check for configuration reloading
+ if ("true".equalsIgnoreCase(Settings
+ .get(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD))) {
+ FileManager.setReloadingConfigs(true);
+ }
+
+ if ("true".equalsIgnoreCase(Settings.get(StrutsConstants.STRUTS_DEVMODE))) {
+ Settings.set(StrutsConstants.STRUTS_I18N_RELOAD, "true");
+ Settings.set(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, "true");
+ }
+
+ if (Settings.isSet(StrutsConstants.STRUTS_OBJECTFACTORY)) {
+ String className = (String) Settings
+ .get(StrutsConstants.STRUTS_OBJECTFACTORY);
+ if (className.equals("spring")) {
+ // note: this class name needs to be in string form so we don't put hard
+ // dependencies on spring, since it isn't technically required.
+ className = "org.apache.struts2.spring.StrutsSpringObjectFactory";
+ } else if (className.equals("plexus")) {
+ // note: this class name needs to be in string form so we don't put hard
+ // dependencies on spring, since it isn't technically required.
+ className = "org.apache.struts2.plexus.PlexusObjectFactory";
+ }
+
+ try {
+ Class clazz = ClassLoaderUtil.loadClass(className,
+ Jsr168Dispatcher.class);
+ ObjectFactory objectFactory = (ObjectFactory) clazz
+ .newInstance();
+ if (objectFactory instanceof ObjectFactoryInitializable) {
+ ((ObjectFactoryInitializable) objectFactory)
+ .init(ServletContextHolderListener
+ .getServletContext());
+ }
+ ObjectFactory.setObjectFactory(objectFactory);
+ } catch (Exception e) {
+ LOG.error("Could not load ObjectFactory named " + className
+ + ". Using default ObjectFactory.", e);
+ }
+ }
+ Dispatcher.setPortletSupportActive(true);
+ dispatcherUtils = new Dispatcher(ServletContextHolderListener.getServletContext());
+ }
+
+ /**
+ * Parse the mode to namespace mappings configured in portlet.xml
+ * @param portletConfig The PortletConfig
+ * @param portletMode The PortletMode
+ * @param nameSpaceParam Name of the init parameter where the namespace for the mode
+ * is configured.
+ * @param defaultActionParam Name of the init parameter where the default action to
+ * execute for the mode is configured.
+ */
+ private void parseModeConfig(PortletConfig portletConfig,
+ PortletMode portletMode, String nameSpaceParam,
+ String defaultActionParam) {
+ String namespace = portletConfig.getInitParameter(nameSpaceParam);
+ if (StringUtils.isEmpty(namespace)) {
+ namespace = "";
+ }
+ modeMap.put(portletMode, namespace);
+ String defaultAction = portletConfig
+ .getInitParameter(defaultActionParam);
+ if (StringUtils.isEmpty(defaultAction)) {
+ defaultAction = DEFAULT_ACTION_NAME;
+ }
+ StringBuffer fullPath = new StringBuffer();
+ if (StringUtils.isNotEmpty(portletNamespace)) {
+ fullPath.append(portletNamespace + "/");
+ }
+ if (StringUtils.isNotEmpty(namespace)) {
+ fullPath.append(namespace + "/");
+ }
+ fullPath.append(defaultAction);
+ ActionMapping mapping = new ActionMapping();
+ mapping.setName(getActionName(fullPath.toString()));
+ mapping.setNamespace(getNamespace(fullPath.toString()));
+ actionMap.put(portletMode, mapping);
+ }
+
+ /**
+ * Service an action from the event phase.
+ *
+ * @see javax.portlet.Portlet#processAction(javax.portlet.ActionRequest,
+ * javax.portlet.ActionResponse)
+ */
+ public void processAction(ActionRequest request, ActionResponse response)
+ throws PortletException, IOException {
+ LOG.debug("Entering processAction");
+ resetActionContext();
+ try {
+ serviceAction(request, response, getActionMapping(request),
+ getRequestMap(request), getParameterMap(request),
+ getSessionMap(request), getApplicationMap(),
+ portletNamespace, EVENT_PHASE);
+ LOG.debug("Leaving processAction");
+ } finally {
+ ActionContext.setContext(null);
+ }
+ }
+
+ /**
+ * Service an action from the render phase.
+ *
+ * @see javax.portlet.Portlet#render(javax.portlet.RenderRequest,
+ * javax.portlet.RenderResponse)
+ */
+ public void render(RenderRequest request, RenderResponse response)
+ throws PortletException, IOException {
+
+ LOG.debug("Entering render");
+ resetActionContext();
+ response.setTitle(getTitle(request));
+ try {
+ // Check to see if an event set the render to be included directly
+ serviceAction(request, response, getActionMapping(request),
+ getRequestMap(request), getParameterMap(request),
+ getSessionMap(request), getApplicationMap(),
+ portletNamespace, RENDER_PHASE);
+ LOG.debug("Leaving render");
+ } finally {
+ resetActionContext();
+ }
+ }
+
+ /**
+ * Reset the action context.
+ */
+ private void resetActionContext() {
+ ActionContext.setContext(null);
+ }
+
+ /**
+ * Merges all application and portlet attributes into a single
+ * HashMap to represent the entire Action context.
+ *
+ * @param requestMap a Map of all request attributes.
+ * @param parameterMap a Map of all request parameters.
+ * @param sessionMap a Map of all session attributes.
+ * @param applicationMap a Map of all servlet context attributes.
+ * @param request the PortletRequest object.
+ * @param response the PortletResponse object.
+ * @param portletConfig the PortletConfig object.
+ * @param phase The portlet phase (render or action, see
+ * {@link PortletActionConstants})
+ * @return a HashMap representing the Action context.
+ */
+ public HashMap createContextMap(Map requestMap, Map parameterMap,
+ Map sessionMap, Map applicationMap, PortletRequest request,
+ PortletResponse response, PortletConfig portletConfig, Integer phase) {
+
+ // TODO Must put http request/response objects into map for use with
+ // ServletActionContext
+ HashMap extraContext = new HashMap();
+ extraContext.put(ActionContext.PARAMETERS, parameterMap);
+ extraContext.put(ActionContext.SESSION, sessionMap);
+ extraContext.put(ActionContext.APPLICATION, applicationMap);
+
+ Locale locale = null;
+ if (Settings.isSet(StrutsConstants.STRUTS_LOCALE)) {
+ locale = LocalizedTextUtil.localeFromString(Settings.get(StrutsConstants.STRUTS_LOCALE), request.getLocale());
+ } else {
+ locale = request.getLocale();
+ }
+ extraContext.put(ActionContext.LOCALE, locale);
+
+ extraContext.put(StrutsStatics.STRUTS_PORTLET_CONTEXT, getPortletContext());
+ extraContext.put(ActionContext.DEV_MODE, Boolean.valueOf(Settings.get(StrutsConstants.STRUTS_DEVMODE)));
+ extraContext.put(REQUEST, request);
+ extraContext.put(RESPONSE, response);
+ extraContext.put(PORTLET_CONFIG, portletConfig);
+ extraContext.put(PORTLET_NAMESPACE, portletNamespace);
+ extraContext.put(DEFAULT_ACTION_FOR_MODE, actionMap.get(request.getPortletMode()));
+ // helpers to get access to request/session/application scope
+ extraContext.put("request", requestMap);
+ extraContext.put("session", sessionMap);
+ extraContext.put("application", applicationMap);
+ extraContext.put("parameters", parameterMap);
+ extraContext.put(MODE_NAMESPACE_MAP, modeMap);
+
+ extraContext.put(PHASE, phase);
+
+ AttributeMap attrMap = new AttributeMap(extraContext);
+ extraContext.put("attr", attrMap);
+
+ return extraContext;
+ }
+
+ /**
+ * Loads the action and executes it. This method first creates the action
+ * context from the given parameters then loads an ActionProxy
+ * from the given action name and namespace. After that, the action is
+ * executed and output channels throught the response object.
+ *
+ * @param request the HttpServletRequest object.
+ * @param response the HttpServletResponse object.
+ * @param mapping the action mapping.
+ * @param requestMap a Map of request attributes.
+ * @param parameterMap a Map of request parameters.
+ * @param sessionMap a Map of all session attributes.
+ * @param applicationMap a Map of all application attributes.
+ * @param portletNamespace the namespace or context of the action.
+ * @param phase The portlet phase (render or action, see
+ * {@link PortletActionConstants})
+ */
+ public void serviceAction(PortletRequest request, PortletResponse response,
+ ActionMapping mapping, Map requestMap, Map parameterMap,
+ Map sessionMap, Map applicationMap, String portletNamespace,
+ Integer phase) throws PortletException {
+ LOG.debug("serviceAction");
+ Dispatcher.setInstance(dispatcherUtils);
+ HashMap extraContext = createContextMap(requestMap, parameterMap,
+ sessionMap, applicationMap, request, response,
+ getPortletConfig(), phase);
+ String actionName = mapping.getName();
+ String namespace = mapping.getNamespace();
+ try {
+ LOG.debug("Creating action proxy for name = " + actionName
+ + ", namespace = " + namespace);
+ ActionProxy proxy = factory.createActionProxy(
+ dispatcherUtils.getConfigurationManager().getConfiguration(), namespace,
+ actionName, extraContext);
+ request.setAttribute("struts.valueStack", proxy.getInvocation()
+ .getStack());
+ if (PortletActionConstants.RENDER_PHASE.equals(phase)
+ && StringUtils.isNotEmpty(request
+ .getParameter(EVENT_ACTION))) {
+
+ ActionProxy action = (ActionProxy) request.getPortletSession()
+ .getAttribute(EVENT_ACTION);
+ if (action != null) {
+ ValueStack stack = proxy.getInvocation().getStack();
+ Object top = stack.pop();
+ stack.push(action.getInvocation().getAction());
+ stack.push(top);
+ }
+ }
+ proxy.execute();
+ if (PortletActionConstants.EVENT_PHASE.equals(phase)) {
+ // Store the executed action in the session for retrieval in the
+ // render phase.
+ ActionResponse actionResp = (ActionResponse) response;
+ request.getPortletSession().setAttribute(EVENT_ACTION, proxy);
+ actionResp.setRenderParameter(EVENT_ACTION, "true");
+ }
+ } catch (ConfigurationException e) {
+ LOG.error("Could not find action", e);
+ throw new PortletException("Could not find action " + actionName, e);
+ } catch (Exception e) {
+ LOG.error("Could not execute action", e);
+ throw new PortletException("Error executing action " + actionName,
+ e);
+ }
+ }
+
+ /**
+ * Returns a Map of all application attributes. Copies all attributes from
+ * the {@link PortletActionContext}into an {@link ApplicationMap}.
+ *
+ * @return a Map of all application attributes.
+ */
+ protected Map getApplicationMap() {
+ return new PortletApplicationMap(getPortletContext());
+ }
+
+ /**
+ * Gets the namespace of the action from the request. The namespace is the
+ * same as the portlet mode. E.g, view mode is mapped to namespace
+ * view, and edit mode is mapped to the namespace
+ * edit
+ *
+ * @param request the PortletRequest object.
+ * @return the namespace of the action.
+ */
+ protected ActionMapping getActionMapping(PortletRequest request) {
+ ActionMapping mapping = new ActionMapping();
+ if (resetAction(request)) {
+ mapping = (ActionMapping) actionMap.get(request.getPortletMode());
+ } else {
+ String actionPath = request.getParameter(ACTION_PARAM);
+ if (StringUtils.isEmpty(actionPath)) {
+ mapping = (ActionMapping) actionMap.get(request
+ .getPortletMode());
+ } else {
+ String namespace = "";
+ String action = actionPath;
+ int idx = actionPath.lastIndexOf('/');
+ if (idx >= 0) {
+ namespace = actionPath.substring(0, idx);
+ action = actionPath.substring(idx + 1);
+ }
+ mapping.setName(action);
+ mapping.setNamespace(namespace);
+ }
+ }
+ return mapping;
+ }
+
+ /**
+ * Get the namespace part of the action path.
+ * @param actionPath Full path to action
+ * @return The namespace part.
+ */
+ String getNamespace(String actionPath) {
+ int idx = actionPath.lastIndexOf('/');
+ String namespace = "";
+ if (idx >= 0) {
+ namespace = actionPath.substring(0, idx);
+ }
+ return namespace;
+ }
+
+ /**
+ * Get the action name part of the action path.
+ * @param actionPath Full path to action
+ * @return The action name.
+ */
+ String getActionName(String actionPath) {
+ int idx = actionPath.lastIndexOf('/');
+ String action = actionPath;
+ if (idx >= 0) {
+ action = actionPath.substring(idx + 1);
+ }
+ return action;
+ }
+
+ /**
+ * Returns a Map of all request parameters. This implementation just calls
+ * {@link PortletRequest#getParameterMap()}.
+ *
+ * @param request the PortletRequest object.
+ * @return a Map of all request parameters.
+ * @throws IOException if an exception occurs while retrieving the parameter
+ * map.
+ */
+ protected Map getParameterMap(PortletRequest request) throws IOException {
+ return new HashMap(request.getParameterMap());
+ }
+
+ /**
+ * Returns a Map of all request attributes. The default implementation is to
+ * wrap the request in a {@link RequestMap}. Override this method to
+ * customize how request attributes are mapped.
+ *
+ * @param request the PortletRequest object.
+ * @return a Map of all request attributes.
+ */
+ protected Map getRequestMap(PortletRequest request) {
+ return new PortletRequestMap(request);
+ }
+
+ /**
+ * Returns a Map of all session attributes. The default implementation is to
+ * wrap the reqeust in a {@link SessionMap}. Override this method to
+ * customize how session attributes are mapped.
+ *
+ * @param request the PortletRequest object.
+ * @return a Map of all session attributes.
+ */
+ protected Map getSessionMap(PortletRequest request) {
+ return new PortletSessionMap(request);
+ }
+
+ /**
+ * Convenience method to ease testing.
+ * @param factory
+ */
+ protected void setActionProxyFactory(ActionProxyFactory factory) {
+ this.factory = factory;
+ }
+
+ /**
+ * Check to see if the action parameter is valid for the current portlet mode. If the portlet
+ * mode has been changed with the portal widgets, the action name is invalid, since the
+ * action name belongs to the previous executing portlet mode. If this method evaluates to
+ * true the default<Mode>Action is used instead.
+ * @param request The portlet request.
+ * @return true if the action should be reset.
+ */
+ private boolean resetAction(PortletRequest request) {
+ boolean reset = false;
+ Map paramMap = request.getParameterMap();
+ String[] modeParam = (String[]) paramMap.get(MODE_PARAM);
+ if (modeParam != null && modeParam.length == 1) {
+ String originatingMode = modeParam[0];
+ String currentMode = request.getPortletMode().toString();
+ if (!currentMode.equals(originatingMode)) {
+ reset = true;
+ }
+ }
+ return reset;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/result/PortletResult.java b/trunk/core/src/main/java/org/apache/struts2/portlet/result/PortletResult.java
new file mode 100644
index 000000000..ba33f3348
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/result/PortletResult.java
@@ -0,0 +1,243 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet.result;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletException;
+import javax.portlet.PortletRequestDispatcher;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.dispatcher.StrutsResultSupport;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.context.PortletActionContext;
+
+import com.opensymphony.xwork2.ActionInvocation;
+
+/**
+ * Result type that includes a JSP to render.
+ *
+ */
+public class PortletResult extends StrutsResultSupport {
+
+ private static final long serialVersionUID = 434251393926178567L;
+
+ /**
+ * Logger instance.
+ */
+ private static final Log LOG = LogFactory.getLog(PortletResult.class);
+
+ private String contentType = "text/html";
+
+ private String title;
+
+ public PortletResult() {
+ super();
+ }
+
+ public PortletResult(String location) {
+ super(location);
+ }
+
+ /**
+ * Execute the result. Obtains the
+ * {@link javax.portlet.PortletRequestDispatcher}from the
+ * {@link PortletActionContext}and includes the JSP.
+ *
+ * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation)
+ */
+ public void doExecute(String finalLocation,
+ ActionInvocation actionInvocation) throws Exception {
+
+ if (PortletActionContext.isRender()) {
+ executeRenderResult(finalLocation);
+ } else if (PortletActionContext.isEvent()) {
+ executeActionResult(finalLocation, actionInvocation);
+ } else {
+ executeRegularServletResult(finalLocation, actionInvocation);
+ }
+ }
+
+ /**
+ * Executes the regular servlet result.
+ *
+ * @param finalLocation
+ * @param actionInvocation
+ */
+ private void executeRegularServletResult(String finalLocation,
+ ActionInvocation actionInvocation) throws ServletException, IOException {
+ ServletContext ctx = ServletActionContext.getServletContext();
+ HttpServletRequest req = ServletActionContext.getRequest();
+ HttpServletResponse res = ServletActionContext.getResponse();
+ try {
+ ctx.getRequestDispatcher(finalLocation).include(req, res);
+ } catch (ServletException e) {
+ LOG.error("ServletException including " + finalLocation, e);
+ throw e;
+ } catch (IOException e) {
+ LOG.error("IOException while including result '" + finalLocation + "'", e);
+ throw e;
+ }
+ }
+
+ /**
+ * Executes the action result.
+ *
+ * @param finalLocation
+ * @param invocation
+ */
+ protected void executeActionResult(String finalLocation,
+ ActionInvocation invocation) {
+ LOG.debug("Executing result in Event phase");
+ ActionResponse res = PortletActionContext.getActionResponse();
+ LOG.debug("Setting event render parameter: " + finalLocation);
+ if (finalLocation.indexOf('?') != -1) {
+ convertQueryParamsToRenderParams(res, finalLocation
+ .substring(finalLocation.indexOf('?') + 1));
+ finalLocation = finalLocation.substring(0, finalLocation
+ .indexOf('?'));
+ }
+ if (finalLocation.endsWith(".action")) {
+ // View is rendered with a view action...luckily...
+ finalLocation = finalLocation.substring(0, finalLocation
+ .lastIndexOf("."));
+ res.setRenderParameter(PortletActionConstants.ACTION_PARAM, finalLocation);
+ } else {
+ // View is rendered outside an action...uh oh...
+ res.setRenderParameter(PortletActionConstants.ACTION_PARAM, "renderDirect");
+ res.setRenderParameter("location", finalLocation);
+ }
+ res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext
+ .getRequest().getPortletMode().toString());
+ }
+
+ /**
+ * Converts the query params to render params.
+ *
+ * @param response
+ * @param queryParams
+ */
+ protected static void convertQueryParamsToRenderParams(
+ ActionResponse response, String queryParams) {
+ StringTokenizer tok = new StringTokenizer(queryParams, "&");
+ while (tok.hasMoreTokens()) {
+ String token = tok.nextToken();
+ String key = token.substring(0, token.indexOf('='));
+ String value = token.substring(token.indexOf('=') + 1);
+ response.setRenderParameter(key, value);
+ }
+ }
+
+ /**
+ * Executes the render result.
+ *
+ * @param finalLocation
+ * @throws PortletException
+ * @throws IOException
+ */
+ protected void executeRenderResult(final String finalLocation) throws PortletException, IOException {
+ LOG.debug("Executing result in Render phase");
+ PortletConfig cfg = PortletActionContext.getPortletConfig();
+ RenderRequest req = PortletActionContext.getRenderRequest();
+ RenderResponse res = PortletActionContext.getRenderResponse();
+ LOG.debug("PortletConfig: " + cfg);
+ LOG.debug("RenderRequest: " + req);
+ LOG.debug("RenderResponse: " + res);
+ res.setContentType(contentType);
+ if (StringUtils.isNotEmpty(title)) {
+ res.setTitle(title);
+ }
+ LOG.debug("Location: " + finalLocation);
+ PortletRequestDispatcher preparator = cfg.getPortletContext()
+ .getNamedDispatcher("preparator");
+ if(preparator == null) {
+ throw new PortletException("Cannot look up 'preparator' servlet. Make sure that you" +
+ "have configured it correctly in the web.xml file.");
+ }
+ new IncludeTemplate() {
+ protected void when(PortletException e) {
+ LOG.error("PortletException while dispatching to 'preparator' servlet", e);
+ }
+ protected void when(IOException e) {
+ LOG.error("IOException while dispatching to 'preparator' servlet", e);
+ }
+ }.include(preparator, req, res);
+ PortletRequestDispatcher dispatcher = cfg.getPortletContext().getRequestDispatcher(finalLocation);
+ if(dispatcher == null) {
+ throw new PortletException("Could not locate dispatcher for '" + finalLocation + "'");
+ }
+ new IncludeTemplate() {
+ protected void when(PortletException e) {
+ LOG.error("PortletException while dispatching to '" + finalLocation + "'");
+ }
+ protected void when(IOException e) {
+ LOG.error("IOException while dispatching to '" + finalLocation + "'");
+ }
+ }.include(dispatcher, req, res);
+ }
+
+ /**
+ * Sets the content type.
+ *
+ * @param contentType The content type to set.
+ */
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ /**
+ * Sets the title.
+ *
+ * @param title The title to set.
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ static class IncludeTemplate {
+ protected void include(PortletRequestDispatcher dispatcher, RenderRequest req, RenderResponse res) throws PortletException, IOException{
+ try {
+ dispatcher.include(req, res);
+ }
+ catch(PortletException e) {
+ when(e);
+ throw e;
+ }
+ catch(IOException e) {
+ when(e);
+ throw e;
+ }
+ }
+
+ protected void when(PortletException e) {}
+
+ protected void when(IOException e) {}
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java b/trunk/core/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java
new file mode 100644
index 000000000..313abdfbf
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java
@@ -0,0 +1,293 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet.result;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletException;
+import javax.portlet.PortletRequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspFactory;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.config.Settings;
+import org.apache.struts2.dispatcher.StrutsResultSupport;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.context.PortletActionContext;
+import org.apache.struts2.views.JspSupportServlet;
+import org.apache.struts2.views.velocity.VelocityManager;
+import org.apache.velocity.Template;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ *
+ *
+ * Using the Servlet container's {@link JspFactory}, this result mocks a JSP
+ * execution environment and then displays a Velocity template that will be
+ * streamed directly to the servlet output.
+ *
+ *
This result type takes the
+ * following parameters:
+ *
+ *
+ *
+ *
+ *
+ * location (default) - the location of the template to process.
+ *
+ *
+ * parse - true by default. If set to false, the location param
+ * will not be parsed for Ognl expressions.
+ *
+ *
+ *
+ * This result follows the same rules from {@link StrutsResultSupport}.
+ *
+ *
+ *
+ *
+ * Example:
+ *
+ *
+ * <!-- START SNIPPET: example -->
+ * <result name="success" type="velocity">
+ * <param name="location">foo.vm</param>
+ * </result>
+ * <!-- END SNIPPET: example -->
+ *
+ *
+ */
+public class PortletVelocityResult extends StrutsResultSupport {
+
+ private static final long serialVersionUID = -8241086555872212274L;
+
+ private static final Log log = LogFactory
+ .getLog(PortletVelocityResult.class);
+
+ public PortletVelocityResult() {
+ super();
+ }
+
+ public PortletVelocityResult(String location) {
+ super(location);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.struts2.dispatcher.StrutsResultSupport#doExecute(java.lang.String, com.opensymphony.xwork2.ActionInvocation)
+ */
+ public void doExecute(String location, ActionInvocation invocation)
+ throws Exception {
+ if (PortletActionContext.isEvent()) {
+ executeActionResult(location, invocation);
+ } else if (PortletActionContext.isRender()) {
+ executeRenderResult(location, invocation);
+ }
+ }
+
+ /**
+ * Executes the result
+ *
+ * @param location The location string
+ * @param invocation The action invocation
+ */
+ private void executeActionResult(String location,
+ ActionInvocation invocation) {
+ ActionResponse res = PortletActionContext.getActionResponse();
+ // View is rendered outside an action...uh oh...
+ res.setRenderParameter(PortletActionConstants.ACTION_PARAM,
+ "freemarkerDirect");
+ res.setRenderParameter("location", location);
+ res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext
+ .getRequest().getPortletMode().toString());
+
+ }
+
+ /**
+ * Creates a Velocity context from the action, loads a Velocity template and
+ * executes the template. Output is written to the servlet output stream.
+ *
+ * @param finalLocation the location of the Velocity template
+ * @param invocation an encapsulation of the action execution state.
+ * @throws Exception if an error occurs when creating the Velocity context,
+ * loading or executing the template or writing output to the
+ * servlet response stream.
+ */
+ public void executeRenderResult(String finalLocation,
+ ActionInvocation invocation) throws Exception {
+ prepareServletActionContext();
+ ValueStack stack = ActionContext.getContext().getValueStack();
+
+ HttpServletRequest request = ServletActionContext.getRequest();
+ HttpServletResponse response = ServletActionContext.getResponse();
+ JspFactory jspFactory = null;
+ ServletContext servletContext = ServletActionContext
+ .getServletContext();
+ Servlet servlet = JspSupportServlet.jspSupportServlet;
+
+ VelocityManager.getInstance().init(servletContext);
+
+ boolean usedJspFactory = false;
+ PageContext pageContext = (PageContext) ActionContext.getContext().get(
+ ServletActionContext.PAGE_CONTEXT);
+
+ if (pageContext == null && servlet != null) {
+ jspFactory = JspFactory.getDefaultFactory();
+ pageContext = jspFactory.getPageContext(servlet, request, response,
+ null, true, 8192, true);
+ ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT,
+ pageContext);
+ usedJspFactory = true;
+ }
+
+ try {
+ String encoding = getEncoding(finalLocation);
+ String contentType = getContentType(finalLocation);
+
+ if (encoding != null) {
+ contentType = contentType + ";charset=" + encoding;
+ }
+
+ VelocityManager velocityManager = VelocityManager.getInstance();
+ Template t = getTemplate(stack,
+ velocityManager.getVelocityEngine(), invocation,
+ finalLocation, encoding);
+
+ Context context = createContext(velocityManager, stack, request,
+ response, finalLocation);
+ Writer writer = new OutputStreamWriter(response.getOutputStream(),
+ encoding);
+
+ response.setContentType(contentType);
+
+ t.merge(context, writer);
+
+ // always flush the writer (we used to only flush it if this was a
+ // jspWriter, but someone asked
+ // to do it all the time (WW-829). Since Velocity support is being
+ // deprecated, we'll oblige :)
+ writer.flush();
+ } catch (Exception e) {
+ log.error("Unable to render Velocity Template, '" + finalLocation
+ + "'", e);
+ throw e;
+ } finally {
+ if (usedJspFactory) {
+ jspFactory.releasePageContext(pageContext);
+ }
+ }
+
+ return;
+ }
+
+ /**
+ * Retrieve the content type for this template.
People can override
+ * this method if they want to provide specific content types for specific
+ * templates (eg text/xml).
+ *
+ * @return The content type associated with this template (default
+ * "text/html")
+ */
+ protected String getContentType(String templateLocation) {
+ return "text/html";
+ }
+
+ /**
+ * Retrieve the encoding for this template.
People can override this
+ * method if they want to provide specific encodings for specific templates.
+ *
+ * @return The encoding associated with this template (defaults to the value
+ * of 'struts.i18n.encoding' property)
+ */
+ protected String getEncoding(String templateLocation) {
+ String encoding = (String) Settings
+ .get(StrutsConstants.STRUTS_I18N_ENCODING);
+ if (encoding == null) {
+ encoding = System.getProperty("file.encoding");
+ }
+ if (encoding == null) {
+ encoding = "UTF-8";
+ }
+ return encoding;
+ }
+
+ /**
+ * Given a value stack, a Velocity engine, and an action invocation, this
+ * method returns the appropriate Velocity template to render.
+ *
+ * @param stack the value stack to resolve the location again (when parse
+ * equals true)
+ * @param velocity the velocity engine to process the request against
+ * @param invocation an encapsulation of the action execution state.
+ * @param location the location of the template
+ * @param encoding the charset encoding of the template
+ * @return the template to render
+ * @throws Exception when the requested template could not be found
+ */
+ protected Template getTemplate(ValueStack stack,
+ VelocityEngine velocity, ActionInvocation invocation,
+ String location, String encoding) throws Exception {
+ if (!location.startsWith("/")) {
+ location = invocation.getProxy().getNamespace() + "/" + location;
+ }
+
+ Template template = velocity.getTemplate(location, encoding);
+
+ return template;
+ }
+
+ /**
+ * Creates the VelocityContext that we'll use to render this page.
+ *
+ * @param velocityManager a reference to the velocityManager to use
+ * @param stack the value stack to resolve the location against (when parse
+ * equals true)
+ * @param location the name of the template that is being used
+ * @return the a minted Velocity context.
+ */
+ protected Context createContext(VelocityManager velocityManager,
+ ValueStack stack, HttpServletRequest request,
+ HttpServletResponse response, String location) {
+ return velocityManager.createContext(stack, request, response);
+ }
+
+ /**
+ * Prepares the servlet action context for this request
+ */
+ private void prepareServletActionContext() throws PortletException,
+ IOException {
+ PortletRequestDispatcher disp = PortletActionContext.getPortletConfig()
+ .getPortletContext().getNamedDispatcher("preparator");
+ disp.include(PortletActionContext.getRenderRequest(),
+ PortletActionContext.getRenderResponse());
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/portlet/util/PortletUrlHelper.java b/trunk/core/src/main/java/org/apache/struts2/portlet/util/PortletUrlHelper.java
new file mode 100644
index 000000000..ee3920ceb
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/portlet/util/PortletUrlHelper.java
@@ -0,0 +1,299 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.portlet.util;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.portlet.PortletMode;
+import javax.portlet.PortletSecurityException;
+import javax.portlet.PortletURL;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+import javax.portlet.WindowState;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.StrutsException;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.context.PortletActionContext;
+
+/**
+ * Helper class for creating Portlet URLs. Portlet URLs are fundamentally different from regular
+ * servlet URLs since they never target the application itself; all requests go through the portlet
+ * container and must therefore be programatically constructed using the
+ * {@link javax.portlet.RenderResponse#createActionURL()} and
+ * {@link javax.portlet.RenderResponse#createRenderURL()} APIs.
+ *
+ */
+public class PortletUrlHelper {
+ public static final String ENCODING = "UTF-8";
+
+ private static final Log LOG = LogFactory.getLog(PortletUrlHelper.class);
+
+ /**
+ * Create a portlet URL with for the specified action and namespace.
+ *
+ * @param action The action the URL should invoke.
+ * @param namespace The namespace of the action to invoke.
+ * @param params The parameters of the URL.
+ * @param type The type of the url, either action or render
+ * @param mode The PortletMode of the URL.
+ * @param state The WindowState of the URL.
+ * @return The URL String.
+ */
+ public static String buildUrl(String action, String namespace, Map params,
+ String type, String mode, String state) {
+ return buildUrl(action, namespace, params, null, type, mode, state,
+ true, true);
+ }
+
+ /**
+ * Create a portlet URL with for the specified action and namespace.
+ *
+ * @see #buildUrl(String, String, Map, String, String, String)
+ */
+ public static String buildUrl(String action, String namespace, Map params,
+ String scheme, String type, String portletMode, String windowState,
+ boolean includeContext, boolean encodeResult) {
+ RenderRequest request = PortletActionContext.getRenderRequest();
+ RenderResponse response = PortletActionContext.getRenderResponse();
+ LOG.debug("Creating url. Action = " + action + ", Namespace = "
+ + namespace + ", Type = " + type);
+ namespace = prependNamespace(namespace, portletMode);
+ if(StringUtils.isEmpty(portletMode)) {
+ portletMode = PortletActionContext.getRenderRequest().getPortletMode().toString();
+ }
+ String result = null;
+ int paramStartIndex = action.indexOf('?');
+ if (paramStartIndex > 0) {
+ String value = action;
+ action = value.substring(0, value.indexOf('?'));
+ String queryStr = value.substring(paramStartIndex + 1);
+ StringTokenizer tok = new StringTokenizer(queryStr, "&");
+ while (tok.hasMoreTokens()) {
+ String paramVal = tok.nextToken();
+ String key = paramVal.substring(0, paramVal.indexOf('='));
+ String val = paramVal.substring(paramVal.indexOf('=') + 1);
+ params.put(key, new String[] { val });
+ }
+ }
+ if (StringUtils.isNotEmpty(namespace)) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(namespace);
+ if(!action.startsWith("/") && !namespace.endsWith("/")) {
+ sb.append("/");
+ }
+ action = sb.append(action).toString();
+ LOG.debug("Resulting actionPath: " + action);
+ }
+ params.put(PortletActionConstants.ACTION_PARAM, new String[] { action });
+
+ PortletURL url = null;
+ if ("action".equalsIgnoreCase(type)) {
+ LOG.debug("Creating action url");
+ url = response.createActionURL();
+ } else {
+ LOG.debug("Creating render url");
+ url = response.createRenderURL();
+ }
+
+ params.put(PortletActionConstants.MODE_PARAM, portletMode);
+ url.setParameters(ensureParamsAreStringArrays(params));
+
+ if ("HTTPS".equalsIgnoreCase(scheme)) {
+ try {
+ url.setSecure(true);
+ } catch (PortletSecurityException e) {
+ LOG.error("Cannot set scheme to https", e);
+ }
+ }
+ try {
+ url.setPortletMode(getPortletMode(request, portletMode));
+ url.setWindowState(getWindowState(request, windowState));
+ } catch (Exception e) {
+ LOG.error("Unable to set mode or state:" + e.getMessage(), e);
+ }
+ result = url.toString();
+ // TEMP BUG-WORKAROUND FOR DOUBLE ESCAPING OF AMPERSAND
+ if(result.indexOf("&") >= 0) {
+ result = StringUtils.replace(result, "&", "&");
+ }
+ return result;
+
+ }
+
+ /**
+ *
+ * Prepend the namespace configuration for the specified namespace and PortletMode.
+ *
+ * @param namespace The base namespace.
+ * @param portletMode The PortletMode.
+ *
+ * @return prepended namespace.
+ */
+ private static String prependNamespace(String namespace, String portletMode) {
+ StringBuffer sb = new StringBuffer();
+ PortletMode mode = PortletActionContext.getRenderRequest().getPortletMode();
+ if(StringUtils.isNotEmpty(portletMode)) {
+ mode = new PortletMode(portletMode);
+ }
+ String portletNamespace = PortletActionContext.getPortletNamespace();
+ String modeNamespace = (String)PortletActionContext.getModeNamespaceMap().get(mode);
+ LOG.debug("PortletNamespace: " + portletNamespace + ", modeNamespace: " + modeNamespace);
+ if(StringUtils.isNotEmpty(portletNamespace)) {
+ sb.append(portletNamespace);
+ }
+ if(StringUtils.isNotEmpty(modeNamespace)) {
+ if(!modeNamespace.startsWith("/")) {
+ sb.append("/");
+ }
+ sb.append(modeNamespace);
+ }
+ if(StringUtils.isNotEmpty(namespace)) {
+ if(!namespace.startsWith("/")) {
+ sb.append("/");
+ }
+ sb.append(namespace);
+ }
+ LOG.debug("Resulting namespace: " + sb);
+ return sb.toString();
+ }
+
+ /**
+ * Encode an url to a non Struts action resource, like stylesheet, image or
+ * servlet.
+ *
+ * @param value
+ * @return encoded url to non Struts action resources.
+ */
+ public static String buildResourceUrl(String value, Map params) {
+ StringBuffer sb = new StringBuffer();
+ // Relative URLs are not allowed in a portlet
+ if (!value.startsWith("/")) {
+ sb.append("/");
+ }
+ sb.append(value);
+ if(params != null && params.size() > 0) {
+ sb.append("?");
+ Iterator it = params.keySet().iterator();
+ try {
+ while(it.hasNext()) {
+ String key = (String)it.next();
+ String val = (String)params.get(key);
+
+ sb.append(URLEncoder.encode(key, ENCODING)).append("=");
+ sb.append(URLEncoder.encode(val, ENCODING));
+ if(it.hasNext()) {
+ sb.append("&");
+ }
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw new StrutsException("Encoding "+ENCODING+" not found");
+ }
+ }
+ RenderResponse resp = PortletActionContext.getRenderResponse();
+ RenderRequest req = PortletActionContext.getRenderRequest();
+ return resp.encodeURL(req.getContextPath() + sb.toString());
+ }
+
+ /**
+ * Will ensure that all entries in params are String arrays,
+ * as requried by the setParameters on the PortletURL.
+ *
+ * @param params The parameters to the URL.
+ * @return A Map with all parameters as String arrays.
+ */
+ public static Map ensureParamsAreStringArrays(Map params) {
+ Map result = null;
+ if (params != null) {
+ result = new HashMap(params.size());
+ Iterator it = params.keySet().iterator();
+ while (it.hasNext()) {
+ Object key = it.next();
+ Object val = params.get(key);
+ if (val instanceof String[]) {
+ result.put(key, val);
+ } else {
+ result.put(key, new String[] { val.toString() });
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Convert the given String to a WindowState object.
+ *
+ * @param portletReq The RenderRequest.
+ * @param windowState The WindowState as a String.
+ * @return The WindowState that mathces the windowState String, or if
+ * the Sring is blank, the current WindowState.
+ */
+ private static WindowState getWindowState(RenderRequest portletReq,
+ String windowState) {
+ WindowState state = portletReq.getWindowState();
+ if (StringUtils.isNotEmpty(windowState)) {
+ state = portletReq.getWindowState();
+ if ("maximized".equalsIgnoreCase(windowState)) {
+ state = WindowState.MAXIMIZED;
+ } else if ("normal".equalsIgnoreCase(windowState)) {
+ state = WindowState.NORMAL;
+ } else if ("minimized".equalsIgnoreCase(windowState)) {
+ state = WindowState.MINIMIZED;
+ }
+ }
+ if(state == null) {
+ state = WindowState.NORMAL;
+ }
+ return state;
+ }
+
+ /**
+ * Convert the given String to a PortletMode object.
+ *
+ * @param portletReq The RenderRequest.
+ * @param portletMode The PortletMode as a String.
+ * @return The PortletMode that mathces the portletMode String, or if
+ * the Sring is blank, the current PortletMode.
+ */
+ private static PortletMode getPortletMode(RenderRequest portletReq,
+ String portletMode) {
+ PortletMode mode = portletReq.getPortletMode();
+
+ if (StringUtils.isNotEmpty(portletMode)) {
+ mode = portletReq.getPortletMode();
+ if ("edit".equalsIgnoreCase(portletMode)) {
+ mode = PortletMode.EDIT;
+ } else if ("view".equalsIgnoreCase(portletMode)) {
+ mode = PortletMode.VIEW;
+ } else if ("help".equalsIgnoreCase(portletMode)) {
+ mode = PortletMode.HELP;
+ }
+ }
+ if(mode == null) {
+ mode = PortletMode.VIEW;
+ }
+ return mode;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/spring/StrutsSpringObjectFactory.java b/trunk/core/src/main/java/org/apache/struts2/spring/StrutsSpringObjectFactory.java
new file mode 100644
index 000000000..45d1e4d30
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/spring/StrutsSpringObjectFactory.java
@@ -0,0 +1,85 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.spring;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.config.Settings;
+import org.apache.struts2.util.ObjectFactoryInitializable;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+import com.opensymphony.xwork2.spring.SpringObjectFactory;
+
+
+
+/**
+ * Struts object factory that integrates with Spring.
+ *
+ * Spring should be loaded using a web context listener
+ * org.springframework.web.context.ContextLoaderListener defined in web.xml.
+ *
+ */
+public class StrutsSpringObjectFactory extends SpringObjectFactory implements ObjectFactoryInitializable {
+ private static final Log log = LogFactory.getLog(StrutsSpringObjectFactory.class);
+
+ /* (non-Javadoc)
+ * @see org.apache.struts2.util.ObjectFactoryInitializable#init(javax.servlet.ServletContext)
+ */
+ public void init(ServletContext servletContext) {
+ log.info("Initializing Struts-Spring integration...");
+
+ ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
+ if (appContext == null) {
+ // uh oh! looks like the lifecycle listener wasn't installed. Let's inform the user
+ String message = "********** FATAL ERROR STARTING UP SPRING-STRUTS INTEGRATION **********\n" +
+ "Looks like the Spring listener was not configured for your web app! \n" +
+ "Nothing will work until WebApplicationContextUtils returns a valid ApplicationContext.\n" +
+ "You might need to add the following to web.xml: \n" +
+ " \n" +
+ " org.springframework.web.context.ContextLoaderListener \n" +
+ " ";
+ log.fatal(message);
+ return;
+ }
+
+ this.setApplicationContext(appContext);
+
+ String autoWire = Settings.get(StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_AUTOWIRE);
+ int type = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME; // default
+ if ("name".equals(autoWire)) {
+ type = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
+ } else if ("type".equals(autoWire)) {
+ type = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
+ } else if ("auto".equals(autoWire)) {
+ type = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
+ } else if ("constructor".equals(autoWire)) {
+ type = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
+ }
+ this.setAutowireStrategy(type);
+
+ boolean useClassCache = "true".equals(Settings.get(StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_USE_CLASS_CACHE));
+ this.setUseClassCache(useClassCache);
+
+ log.info("... initialized Struts-Spring integration successfully");
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/spring/lifecycle/SpringExternalReferenceResolverSetupListener.java b/trunk/core/src/main/java/org/apache/struts2/spring/lifecycle/SpringExternalReferenceResolverSetupListener.java
new file mode 100644
index 000000000..b98688508
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/spring/lifecycle/SpringExternalReferenceResolverSetupListener.java
@@ -0,0 +1,114 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.spring.lifecycle;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.apache.struts2.dispatcher.Dispatcher;
+import org.apache.struts2.dispatcher.DispatcherListener;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+import com.opensymphony.xwork2.config.Configuration;
+import com.opensymphony.xwork2.config.ExternalReferenceResolver;
+import com.opensymphony.xwork2.config.entities.PackageConfig;
+
+/**
+ * Setup any {@link com.opensymphony.xwork2.config.ExternalReferenceResolver}s
+ * that implement the ApplicationContextAware interface from the Spring
+ * framework. Relies on Spring's
+ * {@link org.springframework.web.context.ContextLoaderListener}having been
+ * called first.
+ */
+public class SpringExternalReferenceResolverSetupListener implements
+ ServletContextListener {
+
+ private Map listeners = new HashMap();
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
+ */
+ public synchronized void contextDestroyed(ServletContextEvent event) {
+ Listener l = listeners.get(event.getServletContext());
+ Dispatcher.removeDispatcherListener(l);
+ listeners.remove(event.getServletContext());
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
+ */
+ public synchronized void contextInitialized(ServletContextEvent event) {
+ Listener l = new Listener(event.getServletContext());
+ Dispatcher.addDispatcherListener(l);
+ listeners.put(event.getServletContext(), l);
+ }
+
+ /**
+ * Handles initializing and cleaning up the dispatcher
+ * @author brownd
+ *
+ */
+ private class Listener implements DispatcherListener {
+
+ private ServletContext servletContext;
+
+ /**
+ * Constructs the listener
+ *
+ * @param ctx The servlet context
+ */
+ public Listener(ServletContext ctx) {
+ this.servletContext = ctx;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.struts2.dispatcher.DispatcherListener#dispatcherInitialized(org.apache.struts2.dispatcher.Dispatcher)
+ */
+ public void dispatcherInitialized(Dispatcher du) {
+ ApplicationContext appContext = WebApplicationContextUtils
+ .getWebApplicationContext(servletContext);
+
+ Configuration xworkConfig = du.getConfigurationManager().getConfiguration();
+ Map packageConfigs = xworkConfig.getPackageConfigs();
+ Iterator i = packageConfigs.values().iterator();
+
+ while (i.hasNext()) {
+ PackageConfig packageConfig = (PackageConfig) i.next();
+ ExternalReferenceResolver resolver = packageConfig.getExternalRefResolver();
+ if (resolver == null || !(resolver instanceof ApplicationContextAware))
+ continue;
+ ApplicationContextAware contextAware = (ApplicationContextAware) resolver;
+ contextAware.setApplicationContext(appContext);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.struts2.dispatcher.DispatcherListener#dispatcherDestroyed(org.apache.struts2.dispatcher.Dispatcher)
+ */
+ public void dispatcherDestroyed(Dispatcher du) {
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/AppendIteratorFilter.java b/trunk/core/src/main/java/org/apache/struts2/util/AppendIteratorFilter.java
new file mode 100644
index 000000000..a148e6598
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/AppendIteratorFilter.java
@@ -0,0 +1,81 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.opensymphony.xwork2.Action;
+
+
+/**
+ * A bean that takes several iterators and outputs them in sequence
+ *
+ * @see org.apache.struts2.components.AppendIterator
+ * @see org.apache.struts2.views.jsp.iterator.AppendIteratorTag
+ */
+public class AppendIteratorFilter extends IteratorFilterSupport implements Iterator, Action {
+
+ List iterators = new ArrayList();
+
+ // Attributes ----------------------------------------------------
+ List sources = new ArrayList();
+
+
+ // Public --------------------------------------------------------
+ public void setSource(Object anIterator) {
+ sources.add(anIterator);
+ }
+
+ // Action implementation -----------------------------------------
+ public String execute() {
+ // Make source transformations
+ for (int i = 0; i < sources.size(); i++) {
+ Object source = sources.get(i);
+ iterators.add(getIterator(source));
+ }
+
+ return SUCCESS;
+ }
+
+ // Iterator implementation ---------------------------------------
+ public boolean hasNext() {
+ if (iterators.size() > 0) {
+ return (((Iterator) iterators.get(0)).hasNext());
+ } else {
+ return false;
+ }
+ }
+
+ public Object next() {
+ try {
+ return ((Iterator) iterators.get(0)).next();
+ } finally {
+ if (iterators.size() > 0) {
+ if (!((Iterator) iterators.get(0)).hasNext()) {
+ iterators.remove(0);
+ }
+ }
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/AttributeMap.java b/trunk/core/src/main/java/org/apache/struts2/util/AttributeMap.java
new file mode 100644
index 000000000..93b04ccdf
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/AttributeMap.java
@@ -0,0 +1,134 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.jsp.PageContext;
+
+import org.apache.struts2.ServletActionContext;
+
+
+/**
+ * A Map that holds 4 levels of scope.
+ *
+ * The scopes are the ones known in the web world.:
+ *
+ * Page scope
+ * Request scope
+ * Session scope
+ * Application scope
+ *
+ * A object is searched in the order above, starting from page and ending at application scope.
+ *
+ */
+public class AttributeMap implements Map {
+
+ protected static final String UNSUPPORTED = "method makes no sense for a simplified map";
+
+
+ Map context;
+
+
+ public AttributeMap(Map context) {
+ this.context = context;
+ }
+
+
+ public boolean isEmpty() {
+ throw new UnsupportedOperationException(UNSUPPORTED);
+ }
+
+ public void clear() {
+ throw new UnsupportedOperationException(UNSUPPORTED);
+ }
+
+ public boolean containsKey(Object key) {
+ return (get(key) != null);
+ }
+
+ public boolean containsValue(Object value) {
+ throw new UnsupportedOperationException(UNSUPPORTED);
+ }
+
+ public Set entrySet() {
+ return Collections.EMPTY_SET;
+ }
+
+ public Object get(Object key) {
+ PageContext pc = getPageContext();
+
+ if (pc == null) {
+ Map request = (Map) context.get("request");
+ Map session = (Map) context.get("session");
+ Map application = (Map) context.get("application");
+
+ if ((request != null) && (request.get(key) != null)) {
+ return request.get(key);
+ } else if ((session != null) && (session.get(key) != null)) {
+ return session.get(key);
+ } else if ((application != null) && (application.get(key) != null)) {
+ return application.get(key);
+ }
+ } else {
+ try{
+ return pc.findAttribute(key.toString());
+ }catch (NullPointerException npe){
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ public Set keySet() {
+ return Collections.EMPTY_SET;
+ }
+
+ public Object put(Object key, Object value) {
+ PageContext pc = getPageContext();
+ if (pc != null) {
+ pc.setAttribute(key.toString(), value);
+ }
+
+ return null;
+ }
+
+ public void putAll(Map t) {
+ throw new UnsupportedOperationException(UNSUPPORTED);
+ }
+
+ public Object remove(Object key) {
+ throw new UnsupportedOperationException(UNSUPPORTED);
+ }
+
+ public int size() {
+ throw new UnsupportedOperationException(UNSUPPORTED);
+ }
+
+ public Collection values() {
+ return Collections.EMPTY_SET;
+ }
+
+ private PageContext getPageContext() {
+ return (PageContext) context.get(ServletActionContext.PAGE_CONTEXT);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/ClassLoaderUtils.java b/trunk/core/src/main/java/org/apache/struts2/util/ClassLoaderUtils.java
new file mode 100644
index 000000000..b07e64841
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/ClassLoaderUtils.java
@@ -0,0 +1,131 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+
+/**
+ * This class is extremely useful for loading resources and classes in a fault tolerant manner
+ * that works across different applications servers.
+ *
+ * It has come out of many months of frustrating use of multiple application servers at Atlassian,
+ * please don't change things unless you're sure they're not going to break in one server or another!
+ *
+ */
+public class ClassLoaderUtils {
+
+ /**
+ * Load a given resource.
+ *
+ * This method will try to load the resource using the following methods (in order):
+ *
+ * From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
+ * From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
+ * From the {@link Class#getClassLoader() callingClass.getClassLoader() }
+ *
+ *
+ * @param resourceName The name of the resource to load
+ * @param callingClass The Class object of the calling object
+ */
+ public static URL getResource(String resourceName, Class callingClass) {
+ URL url = null;
+
+ url = Thread.currentThread().getContextClassLoader().getResource(resourceName);
+
+ if (url == null) {
+ url = ClassLoaderUtils.class.getClassLoader().getResource(resourceName);
+ }
+
+ if (url == null) {
+ url = callingClass.getClassLoader().getResource(resourceName);
+ }
+
+ return url;
+ }
+
+ /**
+ * This is a convenience method to load a resource as a stream.
+ *
+ * The algorithm used to find the resource is given in getResource()
+ *
+ * @param resourceName The name of the resource to load
+ * @param callingClass The Class object of the calling object
+ */
+ public static InputStream getResourceAsStream(String resourceName, Class callingClass) {
+ URL url = getResource(resourceName, callingClass);
+
+ try {
+ return (url != null) ? url.openStream() : null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Load a class with a given name.
+ *
+ * It will try to load the class in the following order:
+ *
+ * From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
+ * Using the basic {@link Class#forName(java.lang.String) }
+ * From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
+ * From the {@link Class#getClassLoader() callingClass.getClassLoader() }
+ *
+ *
+ * @param className The name of the class to load
+ * @param callingClass The Class object of the calling object
+ * @throws ClassNotFoundException If the class cannot be found anywhere.
+ */
+ public static Class loadClass(String className, Class callingClass) throws ClassNotFoundException {
+ try {
+ return Thread.currentThread().getContextClassLoader().loadClass(className);
+ } catch (ClassNotFoundException e) {
+ try {
+ return Class.forName(className);
+ } catch (ClassNotFoundException ex) {
+ try {
+ return ClassLoaderUtils.class.getClassLoader().loadClass(className);
+ } catch (ClassNotFoundException exc) {
+ return callingClass.getClassLoader().loadClass(className);
+ }
+ }
+ }
+ }
+
+ /**
+ * Prints the current classloader hierarchy - useful for debugging.
+ */
+ public static void printClassLoader() {
+ System.out.println("ClassLoaderUtils.printClassLoader");
+ printClassLoader(Thread.currentThread().getContextClassLoader());
+ }
+
+ /**
+ * Prints the classloader hierarchy from a given classloader - useful for debugging.
+ */
+ public static void printClassLoader(ClassLoader cl) {
+ System.out.println("ClassLoaderUtils.printClassLoader(cl = " + cl + ")");
+
+ if (cl != null) {
+ printClassLoader(cl.getParent());
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/ContainUtil.java b/trunk/core/src/main/java/org/apache/struts2/util/ContainUtil.java
new file mode 100644
index 000000000..d1093421d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/ContainUtil.java
@@ -0,0 +1,66 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+
+
+/**
+ * ContainUtil will check if object 1 contains object 2.
+ * Object 1 may be an Object, array, Collection, or a Map
+ *
+ */
+public class ContainUtil {
+
+ public static boolean contains(Object obj1, Object obj2) {
+ if ((obj1 == null) || (obj2 == null)) {
+ //log.debug("obj1 or obj2 are null.");
+ return false;
+ }
+
+ if (obj1 instanceof Map) {
+ if (((Map) obj1).containsValue(obj2)) {
+ //log.debug("obj1 is a map and contains obj2");
+ return true;
+ }
+ } else if (obj1 instanceof Collection) {
+ if (((Collection) obj1).contains(obj2)) {
+ //log.debug("obj1 is a collection and contains obj2");
+ return true;
+ }
+ } else if (obj1.getClass().isArray()) {
+ for (int i = 0; i < Array.getLength(obj1); i++) {
+ Object value = null;
+ value = Array.get(obj1, i);
+
+ if (value.equals(obj2)) {
+ //log.debug("obj1 is an array and contains obj2");
+ return true;
+ }
+ }
+ } else if (obj1.equals(obj2)) {
+ //log.debug("obj1 is an object and equals obj2");
+ return true;
+ }
+
+ //log.debug("obj1 does not contain obj2: " + obj1 + ", " + obj2);
+ return false;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/Counter.java b/trunk/core/src/main/java/org/apache/struts2/util/Counter.java
new file mode 100644
index 000000000..ad5e53d0c
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/Counter.java
@@ -0,0 +1,120 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.io.Serializable;
+
+
+/**
+ * A bean that can be used to keep track of a counter.
+ *
+ * Since it is an Iterator it can be used by the iterator tag
+ *
+ */
+public class Counter implements java.util.Iterator, Serializable {
+
+ private static final long serialVersionUID = 2796965884308060179L;
+
+ boolean wrap = false;
+
+ // Attributes ----------------------------------------------------
+ long first = 1;
+ long current = first;
+ long interval = 1;
+ long last = -1;
+
+
+ public void setAdd(long addition) {
+ current += addition;
+ }
+
+ public void setCurrent(long current) {
+ this.current = current;
+ }
+
+ public long getCurrent() {
+ return current;
+ }
+
+ public void setFirst(long first) {
+ this.first = first;
+ current = first;
+ }
+
+ public long getFirst() {
+ return first;
+ }
+
+ public void setInterval(long interval) {
+ this.interval = interval;
+ }
+
+ public long getInterval() {
+ return interval;
+ }
+
+ public void setLast(long last) {
+ this.last = last;
+ }
+
+ public long getLast() {
+ return last;
+ }
+
+ // Public --------------------------------------------------------
+ public long getNext() {
+ long next = current;
+ current += interval;
+
+ if (wrap && (current > last)) {
+ current -= ((1 + last) - first);
+ }
+
+ return next;
+ }
+
+ public long getPrevious() {
+ current -= interval;
+
+ if (wrap && (current < first)) {
+ current += (last - first + 1);
+ }
+
+ return current;
+ }
+
+ public void setWrap(boolean wrap) {
+ this.wrap = wrap;
+ }
+
+ public boolean isWrap() {
+ return wrap;
+ }
+
+ public boolean hasNext() {
+ return ((last == -1) || wrap) ? true : (current <= last);
+ }
+
+ public Object next() {
+ return new Long(getNext());
+ }
+
+ public void remove() {
+ // Do nothing
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/DateFormatter.java b/trunk/core/src/main/java/org/apache/struts2/util/DateFormatter.java
new file mode 100644
index 000000000..61e569f1b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/DateFormatter.java
@@ -0,0 +1,90 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+
+/**
+ * A bean that can be used to format dates
+ *
+ */
+public class DateFormatter {
+
+ Date date;
+ DateFormat format;
+
+ // Attributes ----------------------------------------------------
+ DateFormat parser;
+
+
+ // Public --------------------------------------------------------
+ public DateFormatter() {
+ this.parser = new SimpleDateFormat();
+ this.format = new SimpleDateFormat();
+ this.date = new Date();
+ }
+
+
+ public void setDate(String date) {
+ try {
+ this.date = parser.parse(date);
+ } catch (ParseException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public void setDate(int date) {
+ setDate(Integer.toString(date));
+ }
+
+ public Date getDate() {
+ return this.date;
+ }
+
+ public void setFormat(String format) {
+ this.format = new SimpleDateFormat(format);
+ }
+
+ public void setFormat(DateFormat format) {
+ this.format = format;
+ }
+
+ public String getFormattedDate() {
+ return format.format(date);
+ }
+
+ public void setParseFormat(String format) {
+ this.parser = new SimpleDateFormat(format);
+ }
+
+ public void setParser(DateFormat parser) {
+ this.parser = parser;
+ }
+
+ public void setTime(long time) {
+ date.setTime(time);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/FastByteArrayOutputStream.java b/trunk/core/src/main/java/org/apache/struts2/util/FastByteArrayOutputStream.java
new file mode 100644
index 000000000..e3dde75ef
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/FastByteArrayOutputStream.java
@@ -0,0 +1,217 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+
+/**
+ * A speedy implementation of ByteArrayOutputStream. It's not synchronized, and it
+ * does not copy buffers when it's expanded. There's also no copying of the internal buffer
+ * if it's contents is extracted with the writeTo(stream) method.
+ *
+ */
+public class FastByteArrayOutputStream extends OutputStream {
+
+ // Static --------------------------------------------------------
+ private static final int DEFAULT_BLOCK_SIZE = 8192;
+
+
+ private LinkedList buffers;
+
+ // Attributes ----------------------------------------------------
+ // internal buffer
+ private byte[] buffer;
+
+ // is the stream closed?
+ private boolean closed;
+ private int blockSize;
+ private int index;
+ private int size;
+
+
+ // Constructors --------------------------------------------------
+ public FastByteArrayOutputStream() {
+ this(DEFAULT_BLOCK_SIZE);
+ }
+
+ public FastByteArrayOutputStream(int aSize) {
+ blockSize = aSize;
+ buffer = new byte[blockSize];
+ }
+
+
+ public int getSize() {
+ return size + index;
+ }
+
+ public void close() {
+ closed = true;
+ }
+
+ public byte[] toByteArray() {
+ byte[] data = new byte[getSize()];
+
+ // Check if we have a list of buffers
+ int pos = 0;
+
+ if (buffers != null) {
+ Iterator iter = buffers.iterator();
+
+ while (iter.hasNext()) {
+ byte[] bytes = (byte[]) iter.next();
+ System.arraycopy(bytes, 0, data, pos, blockSize);
+ pos += blockSize;
+ }
+ }
+
+ // write the internal buffer directly
+ System.arraycopy(buffer, 0, data, pos, index);
+
+ return data;
+ }
+
+ public String toString() {
+ return new String(toByteArray());
+ }
+
+ // OutputStream overrides ----------------------------------------
+ public void write(int datum) throws IOException {
+ if (closed) {
+ throw new IOException("Stream closed");
+ } else {
+ if (index == blockSize) {
+ addBuffer();
+ }
+
+ // store the byte
+ buffer[index++] = (byte) datum;
+ }
+ }
+
+ public void write(byte[] data, int offset, int length) throws IOException {
+ if (data == null) {
+ throw new NullPointerException();
+ } else if ((offset < 0) || ((offset + length) > data.length) || (length < 0)) {
+ throw new IndexOutOfBoundsException();
+ } else if (closed) {
+ throw new IOException("Stream closed");
+ } else {
+ if ((index + length) > blockSize) {
+ int copyLength;
+
+ do {
+ if (index == blockSize) {
+ addBuffer();
+ }
+
+ copyLength = blockSize - index;
+
+ if (length < copyLength) {
+ copyLength = length;
+ }
+
+ System.arraycopy(data, offset, buffer, index, copyLength);
+ offset += copyLength;
+ index += copyLength;
+ length -= copyLength;
+ } while (length > 0);
+ } else {
+ // Copy in the subarray
+ System.arraycopy(data, offset, buffer, index, length);
+ index += length;
+ }
+ }
+ }
+
+ // Public
+ public void writeTo(OutputStream out) throws IOException {
+ // Check if we have a list of buffers
+ if (buffers != null) {
+ Iterator iter = buffers.iterator();
+
+ while (iter.hasNext()) {
+ byte[] bytes = (byte[]) iter.next();
+ out.write(bytes, 0, blockSize);
+ }
+ }
+
+ // write the internal buffer directly
+ out.write(buffer, 0, index);
+ }
+
+ public void writeTo(RandomAccessFile out) throws IOException {
+ // Check if we have a list of buffers
+ if (buffers != null) {
+ Iterator iter = buffers.iterator();
+
+ while (iter.hasNext()) {
+ byte[] bytes = (byte[]) iter.next();
+ out.write(bytes, 0, blockSize);
+ }
+ }
+
+ // write the internal buffer directly
+ out.write(buffer, 0, index);
+ }
+
+ public void writeTo(Writer out, String encoding) throws IOException {
+ // Check if we have a list of buffers
+ if (buffers != null) {
+ Iterator iter = buffers.iterator();
+
+ while (iter.hasNext()) {
+ byte[] bytes = (byte[]) iter.next();
+
+ if (encoding != null) {
+ out.write(new String(bytes, encoding));
+ } else {
+ out.write(new String(bytes));
+ }
+ }
+ }
+
+ // write the internal buffer directly
+ if (encoding != null) {
+ out.write(new String(buffer, 0, index, encoding));
+ } else {
+ out.write(new String(buffer, 0, index));
+ }
+ }
+
+ /**
+ * Create a new buffer and store the
+ * current one in linked list
+ */
+ protected void addBuffer() {
+ if (buffers == null) {
+ buffers = new LinkedList();
+ }
+
+ buffers.addLast(buffer);
+
+ buffer = new byte[blockSize];
+ size += index;
+ index = 0;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/InvocationSessionStore.java b/trunk/core/src/main/java/org/apache/struts2/util/InvocationSessionStore.java
new file mode 100644
index 000000000..53119682c
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/InvocationSessionStore.java
@@ -0,0 +1,118 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * InvocationSessionStore
+ *
+ */
+public class InvocationSessionStore {
+
+ private static final String INVOCATION_MAP_KEY = "org.apache.struts2.util.InvocationSessionStore.invocationMap";
+
+
+ private InvocationSessionStore() {
+ }
+
+
+ /**
+ * Checks the Map in the Session for the key and the token. If the
+ * ActionInvocation is saved in the Session, the ValueStack from the
+ * ActionProxy associated with the ActionInvocation is set into the
+ * ActionContext and the ActionInvocation is returned.
+ *
+ * @param key the name the DefaultActionInvocation and ActionContext were saved as
+ * @return the DefaultActionInvocation saved using the key, or null if none was found
+ */
+ public static ActionInvocation loadInvocation(String key, String token) {
+ InvocationContext invocationContext = (InvocationContext) getInvocationMap().get(key);
+
+ if ((invocationContext == null) || !invocationContext.token.equals(token)) {
+ return null;
+ }
+
+ ValueStack stack = invocationContext.invocation.getStack();
+ ActionContext.getContext().setValueStack(stack);
+
+ return invocationContext.invocation;
+ }
+
+ /**
+ * Stores the DefaultActionInvocation and ActionContext into the Session using the provided key for loading later using
+ * {@link #loadInvocation}
+ *
+ * @param key
+ * @param invocation
+ */
+ public static void storeInvocation(String key, String token, ActionInvocation invocation) {
+ InvocationContext invocationContext = new InvocationContext(invocation, token);
+ Map invocationMap = getInvocationMap();
+ invocationMap.put(key, invocationContext);
+ setInvocationMap(invocationMap);
+ }
+
+ static void setInvocationMap(Map invocationMap) {
+ Map session = ActionContext.getContext().getSession();
+
+ if (session == null) {
+ throw new IllegalStateException("Unable to access the session.");
+ }
+
+ session.put(INVOCATION_MAP_KEY, invocationMap);
+ }
+
+ static Map getInvocationMap() {
+ Map session = ActionContext.getContext().getSession();
+
+ if (session == null) {
+ throw new IllegalStateException("Unable to access the session.");
+ }
+
+ Map invocationMap = (Map) session.get(INVOCATION_MAP_KEY);
+
+ if (invocationMap == null) {
+ invocationMap = new HashMap();
+ setInvocationMap(invocationMap);
+ }
+
+ return invocationMap;
+ }
+
+
+ private static class InvocationContext implements Serializable {
+
+ private static final long serialVersionUID = -286697666275777888L;
+
+ ActionInvocation invocation;
+ String token;
+
+ public InvocationContext(ActionInvocation invocation, String token) {
+ this.invocation = invocation;
+ this.token = token;
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/IteratorFilterSupport.java b/trunk/core/src/main/java/org/apache/struts2/util/IteratorFilterSupport.java
new file mode 100644
index 000000000..5f3a26913
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/IteratorFilterSupport.java
@@ -0,0 +1,56 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+
+
+/**
+ * A base class for iterator filters
+ *
+ */
+public abstract class IteratorFilterSupport {
+
+ // Protected implementation --------------------------------------
+ protected Object getIterator(Object source) {
+ return MakeIterator.convert(source);
+ }
+
+
+ // Wrapper for enumerations
+ public class EnumerationIterator implements Iterator {
+ Enumeration enumeration;
+
+ public EnumerationIterator(Enumeration aEnum) {
+ enumeration = aEnum;
+ }
+
+ public boolean hasNext() {
+ return enumeration.hasMoreElements();
+ }
+
+ public Object next() {
+ return enumeration.nextElement();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Remove is not supported in IteratorFilterSupport.");
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/IteratorGenerator.java b/trunk/core/src/main/java/org/apache/struts2/util/IteratorGenerator.java
new file mode 100644
index 000000000..6bf81d650
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/IteratorGenerator.java
@@ -0,0 +1,138 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.opensymphony.xwork2.Action;
+
+
+/**
+ * A bean that generates an iterator filled with a given object depending on the count,
+ * separator and converter defined. It is being used by IteratorGeneratorTag.
+ *
+ */
+public class IteratorGenerator implements Iterator, Action {
+
+ private static final Log _log = LogFactory.getLog(IteratorGenerator.class);
+
+ List values;
+ Object value;
+ String separator;
+ Converter converter;
+
+ // Attributes ----------------------------------------------------
+ int count = 0;
+ int currentCount = 0;
+
+
+ public void setCount(int aCount) {
+ this.count = aCount;
+ }
+
+ public boolean getHasNext() {
+ return hasNext();
+ }
+
+ public Object getNext() {
+ return next();
+ }
+
+ public void setSeparator(String aChar) {
+ separator = aChar;
+ }
+
+ public void setConverter(Converter aConverter) {
+ converter = aConverter;
+ }
+
+ // Public --------------------------------------------------------
+ public void setValues(Object aValue) {
+ value = aValue;
+ }
+
+ // Action implementation -----------------------------------------
+ public String execute() {
+ if (value == null) {
+ return ERROR;
+ } else {
+ values = new ArrayList();
+
+ if (separator != null) {
+ StringTokenizer tokens = new StringTokenizer(value.toString(), separator);
+
+ while (tokens.hasMoreTokens()) {
+ String token = tokens.nextToken().trim();
+ if (converter != null) {
+ try {
+ Object convertedObj = converter.convert(token);
+ values.add(convertedObj);
+ }
+ catch(Exception e) { // make sure things, goes on, we just ignore the bad ones
+ _log.warn("unable to convert ["+token+"], skipping this token, it will not appear in the generated iterator", e);
+ }
+ }
+ else {
+ values.add(token);
+ }
+ }
+ } else {
+ values.add(value.toString());
+ }
+
+ // Count default is the size of the list of values
+ if (count == 0) {
+ count = values.size();
+ }
+
+ return SUCCESS;
+ }
+ }
+
+ // Iterator implementation ---------------------------------------
+ public boolean hasNext() {
+ return (value == null) ? false : ((currentCount < count) || (count == -1));
+ }
+
+ public Object next() {
+ try {
+ return values.get(currentCount % values.size());
+ } finally {
+ currentCount++;
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Remove is not supported in IteratorGenerator.");
+ }
+
+
+ // Inner class --------------------------------------------------
+ /**
+ * Interface for converting each separated token into an Object of choice.
+ */
+ public static interface Converter {
+ Object convert(String token) throws Exception;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/ListEntry.java b/trunk/core/src/main/java/org/apache/struts2/util/ListEntry.java
new file mode 100644
index 000000000..db7b195e7
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/ListEntry.java
@@ -0,0 +1,49 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+/**
+ * Entry in a list.
+ *
+ */
+public class ListEntry {
+
+ final private Object key;
+ final private Object value;
+ final private boolean isSelected;
+
+
+ public ListEntry(Object key, Object value, boolean isSelected) {
+ this.key = key;
+ this.value = value;
+ this.isSelected = isSelected;
+ }
+
+
+ public boolean getIsSelected() {
+ return isSelected;
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/MakeIterator.java b/trunk/core/src/main/java/org/apache/struts2/util/MakeIterator.java
new file mode 100644
index 000000000..fe59ec284
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/MakeIterator.java
@@ -0,0 +1,106 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * MakeIterator.
+ *
+ */
+public class MakeIterator {
+
+ /**
+ * Determine whether a given object can be made into an Iterator
+ *
+ * @param object the object to check
+ * @return true if the object can be converted to an iterator and
+ * false otherwise
+ */
+ public static boolean isIterable(Object object) {
+ if (object == null) {
+ return false;
+ }
+
+ if (object instanceof Map) {
+ return true;
+ } else if (object instanceof Collection) {
+ return true;
+ } else if (object.getClass().isArray()) {
+ return true;
+ } else if (object instanceof Enumeration) {
+ return true;
+ } else if (object instanceof Iterator) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static Iterator convert(Object value) {
+ Iterator iterator;
+
+ if (value instanceof Iterator) {
+ return (Iterator) value;
+ }
+
+ if (value instanceof Map) {
+ value = ((Map) value).entrySet();
+ }
+
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof Collection) {
+ iterator = ((Collection) value).iterator();
+ } else if (value.getClass().isArray()) {
+ //need ability to support primitives; therefore, cannot
+ //use Object[] casting.
+ ArrayList list = new ArrayList(Array.getLength(value));
+
+ for (int j = 0; j < Array.getLength(value); j++) {
+ list.add(Array.get(value, j));
+ }
+
+ iterator = list.iterator();
+ } else if (value instanceof Enumeration) {
+ Enumeration enumeration = (Enumeration) value;
+ ArrayList list = new ArrayList();
+
+ while (enumeration.hasMoreElements()) {
+ list.add(enumeration.nextElement());
+ }
+
+ iterator = list.iterator();
+ } else {
+ List list = new ArrayList(1);
+ list.add(value);
+ iterator = list.iterator();
+ }
+
+ return iterator;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/MergeIteratorFilter.java b/trunk/core/src/main/java/org/apache/struts2/util/MergeIteratorFilter.java
new file mode 100644
index 000000000..e46ec4685
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/MergeIteratorFilter.java
@@ -0,0 +1,87 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.opensymphony.xwork2.Action;
+
+
+/**
+ * A bean that takes several iterators and outputs the merge of them. Used by
+ * MergeIteratorTag.
+ *
+ * @see org.apache.struts2.views.jsp.iterator.MergeIteratorTag
+ * @see org.apache.struts2.components.MergeIterator
+ */
+public class MergeIteratorFilter extends IteratorFilterSupport implements Iterator, Action {
+
+ List iterators = new ArrayList();
+
+ // Attributes ----------------------------------------------------
+ List sources = new ArrayList();
+ int idx = 0;
+
+
+ // Public --------------------------------------------------------
+ public void setSource(Object anIterator) {
+ sources.add(anIterator);
+ }
+
+ // Action implementation -----------------------------------------
+ public String execute() {
+ // Make source transformations
+ for (int i = 0; i < sources.size(); i++) {
+ Object source = sources.get(i);
+ iterators.add(getIterator(source));
+ }
+
+ return SUCCESS;
+ }
+
+ // Iterator implementation ---------------------------------------
+ public boolean hasNext() {
+ while (iterators.size() > 0) {
+ if (((Iterator) iterators.get(idx)).hasNext()) {
+ return true;
+ } else {
+ iterators.remove(idx);
+
+ if (iterators.size() > 0) {
+ idx = idx % iterators.size();
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public Object next() {
+ try {
+ return ((Iterator) iterators.get(idx)).next();
+ } finally {
+ idx = (idx + 1) % iterators.size();
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Remove is not supported in MergeIteratorFilter.");
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/ObjectFactoryDestroyable.java b/trunk/core/src/main/java/org/apache/struts2/util/ObjectFactoryDestroyable.java
new file mode 100644
index 000000000..c2e26b277
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/ObjectFactoryDestroyable.java
@@ -0,0 +1,30 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+/**
+ * An interface to be implemented by any ObjectFactory implementation
+ * if it requires shutdown hook whenever an ObjectFactory is to be
+ * destroyed.
+ *
+ * @see org.apache.struts2.dispatcher.FilterDispatcher
+ * @see org.apache.struts2.dispatcher.Dispatcher
+ */
+public interface ObjectFactoryDestroyable {
+ void destroy();
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/ObjectFactoryInitializable.java b/trunk/core/src/main/java/org/apache/struts2/util/ObjectFactoryInitializable.java
new file mode 100644
index 000000000..7db4f8992
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/ObjectFactoryInitializable.java
@@ -0,0 +1,30 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import javax.servlet.ServletContext;
+
+/**
+ * Used to pass ServletContext init parameters to various
+ * frameworks such as Spring, Plexus and Portlet.
+ */
+public interface ObjectFactoryInitializable {
+
+ void init(ServletContext servletContext);
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/ObjectFactoryLifecycle.java b/trunk/core/src/main/java/org/apache/struts2/util/ObjectFactoryLifecycle.java
new file mode 100644
index 000000000..84356f4d1
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/ObjectFactoryLifecycle.java
@@ -0,0 +1,30 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+/**
+ * An interface indicating the lifecycle of an ObjectFactory implementation.
+ *
+ * @see ObjectFactoryLifecycle
+ * @see com.opensymphony.xwork2.ObjectFactory
+ * @see org.apache.struts2.util.ObjectFactoryInitializable
+ * @see org.apache.struts2.util.ObjectFactoryDestroyable
+ */
+public interface ObjectFactoryLifecycle extends ObjectFactoryInitializable, ObjectFactoryDestroyable {
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/PrefixTrie.java b/trunk/core/src/main/java/org/apache/struts2/util/PrefixTrie.java
new file mode 100644
index 000000000..24288b13b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/PrefixTrie.java
@@ -0,0 +1,63 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+/**
+ * Quickly matches a prefix to an object.
+ *
+ */
+public class PrefixTrie {
+
+ // supports 7-bit chars.
+ private static final int SIZE = 128;
+
+ Node root = new Node();
+
+ public void put(String prefix, Object value) {
+ Node current = root;
+ for (int i = 0; i < prefix.length(); i++) {
+ char c = prefix.charAt(i);
+ if (c > SIZE)
+ throw new IllegalArgumentException("'" + c + "' is too big.");
+ if (current.next[c] == null)
+ current.next[c] = new Node();
+ current = current.next[c];
+ }
+ current.value = value;
+ }
+
+ public Object get(String key) {
+ Node current = root;
+ for (int i = 0; i < key.length(); i++) {
+ char c = key.charAt(i);
+ if (c > SIZE)
+ return null;
+ current = current.next[c];
+ if (current == null)
+ return null;
+ if (current.value != null)
+ return current.value;
+ }
+ return null;
+ }
+
+ static class Node {
+ Object value;
+ Node[] next = new Node[SIZE];
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/ResolverSetupServletContextListener.java b/trunk/core/src/main/java/org/apache/struts2/util/ResolverSetupServletContextListener.java
new file mode 100644
index 000000000..9f26f8651
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/ResolverSetupServletContextListener.java
@@ -0,0 +1,87 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.apache.struts2.dispatcher.Dispatcher;
+import org.apache.struts2.dispatcher.DispatcherListener;
+
+import com.opensymphony.xwork2.config.Configuration;
+import com.opensymphony.xwork2.config.entities.PackageConfig;
+
+
+/**
+ * A Servlet Context Listener that will loop through all Reference Resolvers available in
+ * the xwork Configuration and set the ServletContext on those that are ServletContextAware.
+ * The Servlet Context can be used by the External Reference Resolver to initialise it's state. i.e. the
+ * Spring framework uses a ContextServletListener to initialise it's IoC container, storing it's
+ * container context (ApplicationContext in Spring terms) in the Servlet context, the External
+ * Reference Resolver can get a reference to the container context from the servlet context.
+ */
+public class ResolverSetupServletContextListener implements ServletContextListener {
+
+ Map listeners = new HashMap();
+
+ public synchronized void contextDestroyed(ServletContextEvent event) {
+ Listener l = listeners.get(event.getServletContext());
+ Dispatcher.removeDispatcherListener(l);
+ listeners.remove(event.getServletContext());
+ }
+
+ public synchronized void contextInitialized(ServletContextEvent event) {
+ Listener l = new Listener(event.getServletContext());
+ Dispatcher.addDispatcherListener(l);
+ listeners.put(event.getServletContext(), l);
+ }
+
+ private class Listener implements DispatcherListener {
+
+ private ServletContext servletContext;
+
+ public Listener(ServletContext ctx) {
+ this.servletContext = ctx;
+ }
+
+ public void dispatcherInitialized(Dispatcher du) {
+ Configuration config = du.getConfigurationManager().getConfiguration();
+ String key;
+ PackageConfig packageConfig;
+
+ for (Iterator iter = config.getPackageConfigNames().iterator();
+ iter.hasNext();) {
+ key = (String) iter.next();
+ packageConfig = config.getPackageConfig(key);
+
+ if (packageConfig.getExternalRefResolver()instanceof ServletContextAware) {
+ ((ServletContextAware) packageConfig.getExternalRefResolver()).setServletContext(servletContext);
+ }
+ }
+
+ }
+
+ public void dispatcherDestroyed(Dispatcher du) {
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/ServletContextAware.java b/trunk/core/src/main/java/org/apache/struts2/util/ServletContextAware.java
new file mode 100644
index 000000000..17a60d255
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/ServletContextAware.java
@@ -0,0 +1,29 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import javax.servlet.ServletContext;
+
+
+/**
+ * For components that have a dependence on the Servlet context.
+ */
+public interface ServletContextAware {
+
+ public void setServletContext(ServletContext context);
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/SortIteratorFilter.java b/trunk/core/src/main/java/org/apache/struts2/util/SortIteratorFilter.java
new file mode 100644
index 000000000..9fdf2c5a7
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/SortIteratorFilter.java
@@ -0,0 +1,105 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.logging.LogFactory;
+
+import com.opensymphony.xwork2.Action;
+
+
+/**
+ * A bean that takes a source and comparator then attempt to sort the source
+ * utilizing the comparator. It is being used in SortIteratorTag.
+ *
+ * @see org.apache.struts2.views.jsp.iterator.SortIteratorTag
+ */
+public class SortIteratorFilter extends IteratorFilterSupport implements Iterator, Action {
+
+ Comparator comparator;
+ Iterator iterator;
+ List list;
+
+ // Attributes ----------------------------------------------------
+ Object source;
+
+
+ public void setComparator(Comparator aComparator) {
+ this.comparator = aComparator;
+ }
+
+ public List getList() {
+ return list;
+ }
+
+ // Public --------------------------------------------------------
+ public void setSource(Object anIterator) {
+ source = anIterator;
+ }
+
+ // Action implementation -----------------------------------------
+ public String execute() {
+ if (source == null) {
+ return ERROR;
+ } else {
+ try {
+ if (!MakeIterator.isIterable(source)) {
+ LogFactory.getLog(SortIteratorFilter.class.getName()).warn("Cannot create SortIterator for source " + source);
+
+ return ERROR;
+ }
+
+ list = new ArrayList();
+
+ Iterator i = MakeIterator.convert(source);
+
+ while (i.hasNext()) {
+ list.add(i.next());
+ }
+
+ // Sort it
+ Collections.sort(list, comparator);
+ iterator = list.iterator();
+
+ return SUCCESS;
+ } catch (Exception e) {
+ LogFactory.getLog(SortIteratorFilter.class.getName()).warn("Error creating sort iterator.", e);
+
+ return ERROR;
+ }
+ }
+ }
+
+ // Iterator implementation ---------------------------------------
+ public boolean hasNext() {
+ return (source == null) ? false : iterator.hasNext();
+ }
+
+ public Object next() {
+ return iterator.next();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Remove is not supported in SortIteratorFilter.");
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/Sorter.java b/trunk/core/src/main/java/org/apache/struts2/util/Sorter.java
new file mode 100644
index 000000000..c1e929e01
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/Sorter.java
@@ -0,0 +1,149 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.Comparator;
+
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ValueStackFactory;
+
+
+/**
+ * Sorters. Utility sorters for use with the "sort" tag.
+ *
+ * @see org.apache.struts2.views.jsp.iterator.SortIteratorTag
+ * @see SortIteratorFilter
+ */
+public class Sorter {
+
+ public Comparator getAscending() {
+ return new Comparator() {
+ public int compare(Object o1, Object o2) {
+ if (o1 instanceof Comparable) {
+ return ((Comparable) o1).compareTo(o2);
+ } else {
+ String s1 = o1.toString();
+ String s2 = o2.toString();
+
+ return s1.compareTo(s2);
+ }
+ }
+ };
+ }
+
+ public Comparator getAscending(final String anExpression) {
+ return new Comparator() {
+ private ValueStack stack = ValueStackFactory.getFactory().createValueStack();
+
+ public int compare(Object o1, Object o2) {
+ // Get value for first object
+ stack.push(o1);
+
+ Object v1 = stack.findValue(anExpression);
+ stack.pop();
+
+ // Get value for second object
+ stack.push(o2);
+
+ Object v2 = stack.findValue(anExpression);
+ stack.pop();
+
+ // Ensure non-null
+ if (v1 == null) {
+ v1 = "";
+ }
+
+ if (v2 == null) {
+ v2 = "";
+ }
+
+ // Compare them
+ if (v1 instanceof Comparable && v1.getClass().equals(v2.getClass())) {
+ return ((Comparable) v1).compareTo(v2);
+ } else {
+ String s1 = v1.toString();
+ String s2 = v2.toString();
+
+ return s1.compareTo(s2);
+ }
+ }
+ };
+ }
+
+ public Comparator getComparator(String anExpression, boolean ascending) {
+ if (ascending) {
+ return getAscending(anExpression);
+ } else {
+ return getDescending(anExpression);
+ }
+ }
+
+ public Comparator getDescending() {
+ return new Comparator() {
+ public int compare(Object o1, Object o2) {
+ if (o2 instanceof Comparable) {
+ return ((Comparable) o2).compareTo(o1);
+ } else {
+ String s1 = o1.toString();
+ String s2 = o2.toString();
+
+ return s2.compareTo(s1);
+ }
+ }
+ };
+ }
+
+ public Comparator getDescending(final String anExpression) {
+ return new Comparator() {
+ private ValueStack stack = ValueStackFactory.getFactory().createValueStack();
+
+ public int compare(Object o1, Object o2) {
+ // Get value for first object
+ stack.push(o1);
+
+ Object v1 = stack.findValue(anExpression);
+ stack.pop();
+
+ // Get value for second object
+ stack.push(o2);
+
+ Object v2 = stack.findValue(anExpression);
+ stack.pop();
+
+ // Ensure non-null
+ if (v1 == null) {
+ v1 = "";
+ }
+
+ if (v2 == null) {
+ v2 = "";
+ }
+
+ // Compare them
+ if (v2 instanceof Comparable && v1.getClass().equals(v2.getClass())) {
+ return ((Comparable) v2).compareTo(v1);
+ } else {
+ String s1 = v1.toString();
+ String s2 = v2.toString();
+
+ return s2.compareTo(s1);
+ }
+ }
+ };
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/StrutsTypeConverter.java b/trunk/core/src/main/java/org/apache/struts2/util/StrutsTypeConverter.java
new file mode 100644
index 000000000..e81c3b0d6
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/StrutsTypeConverter.java
@@ -0,0 +1,89 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.Map;
+
+import ognl.DefaultTypeConverter;
+
+/**
+ *
+ *
+ * Base class for type converters used in Struts. This class provides two abstract methods that are used to convert
+ * both to and from strings -- the critical functionality that is core to Struts's type coversion system.
+ *
+ *
Type converters do not have to use this class. It is merely a helper base class, although it is recommended that
+ * you use this class as it provides the common type conversion contract required for all web-based type conversion.
+ *
+ *
There's a hook (fall back method) called performFallbackConversion of which
+ * could be used to perform some fallback conversion if convertValue method of this
+ * failed. By default it just ask its super class (Ognl's DefaultTypeConverter) to do the conversion.
+ *
+ *
To allow the framework to recognize that a conversion error has occurred, throw an XWorkException or
+ * preferable a TypeConversionException.
+ *
+ *
+ *
+ */
+public abstract class StrutsTypeConverter extends DefaultTypeConverter {
+ public Object convertValue(Map context, Object o, Class toClass) {
+ if (toClass.equals(String.class)) {
+ return convertToString(context, o);
+ } else if (o instanceof String[]) {
+ return convertFromString(context, (String[]) o, toClass);
+ } else if (o instanceof String) {
+ return convertFromString(context, new String[]{(String) o}, toClass);
+ } else {
+ return performFallbackConversion(context, o, toClass);
+ }
+ }
+
+ /**
+ * Hook to perform a fallback conversion if every default options failed. By default
+ * this will ask Ognl's DefaultTypeConverter (of which this class extends) to
+ * perform the conversion.
+ *
+ * @param context
+ * @param o
+ * @param toClass
+ * @return The fallback conversion
+ */
+ protected Object performFallbackConversion(Map context, Object o, Class toClass) {
+ return super.convertValue(context, o, toClass);
+ }
+
+
+ /**
+ * Converts one or more String values to the specified class.
+ *
+ * @param context the action context
+ * @param values the String values to be converted, such as those submitted from an HTML form
+ * @param toClass the class to convert to
+ * @return the converted object
+ */
+ public abstract Object convertFromString(Map context, String[] values, Class toClass);
+
+ /**
+ * Converts the specified object to a String.
+ *
+ * @param context the action context
+ * @param o the object to be converted
+ * @return the converted String
+ */
+ public abstract String convertToString(Map context, Object o);
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/StrutsUtil.java b/trunk/core/src/main/java/org/apache/struts2/util/StrutsUtil.java
new file mode 100644
index 000000000..0440e3d78
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/StrutsUtil.java
@@ -0,0 +1,290 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.views.jsp.ui.OgnlTool;
+import org.apache.struts2.views.util.UrlHelper;
+
+import com.opensymphony.xwork2.util.TextUtils;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.ObjectFactory;
+
+
+/**
+ * Struts base utility class, for use in Velocity and Freemarker templates
+ *
+ */
+public class StrutsUtil {
+
+ protected static final Log log = LogFactory.getLog(StrutsUtil.class);
+
+
+ protected HttpServletRequest request;
+ protected HttpServletResponse response;
+ protected Map classes = new Hashtable();
+ protected OgnlTool ognl = OgnlTool.getInstance();
+ protected ValueStack stack;
+
+
+ public StrutsUtil(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
+ this.stack = stack;
+ this.request = request;
+ this.response = response;
+ }
+
+
+ public Object bean(Object aName) throws Exception {
+ String name = aName.toString();
+ Class c = (Class) classes.get(name);
+
+ if (c == null) {
+ c = ClassLoaderUtils.loadClass(name, StrutsUtil.class);
+ classes.put(name, c);
+ }
+
+ return ObjectFactory.getObjectFactory().buildBean(c, stack.getContext());
+ }
+
+ public boolean isTrue(String expression) {
+ Boolean retVal = (Boolean) stack.findValue(expression, Boolean.class);
+ if (retVal == null) {
+ return false;
+ }
+ return retVal.booleanValue();
+ }
+
+ public Object findString(String name) {
+ return stack.findValue(name, String.class);
+ }
+
+ public String include(Object aName) throws Exception {
+ return include(aName, request, response);
+ }
+
+ /**
+ * @deprecated the request and response are stored in this util class, please use include(string)
+ */
+ public String include(Object aName, HttpServletRequest aRequest, HttpServletResponse aResponse) throws Exception {
+ try {
+ RequestDispatcher dispatcher = aRequest.getRequestDispatcher(aName.toString());
+
+ if (dispatcher == null) {
+ throw new IllegalArgumentException("Cannot find included file " + aName);
+ }
+
+ ResponseWrapper responseWrapper = new ResponseWrapper(aResponse);
+
+ dispatcher.include(aRequest, responseWrapper);
+
+ return responseWrapper.getData();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+
+ public String urlEncode(String s) {
+ try {
+ return URLEncoder.encode(s, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ return s;
+ }
+ }
+
+ public String buildUrl(String url) {
+ return UrlHelper.buildUrl(url, request, response, null);
+ }
+
+ public Object findValue(String expression, String className) throws ClassNotFoundException {
+ return stack.findValue(expression, Class.forName(className));
+ }
+
+ public String getText(String text) {
+ return (String) stack.findValue("getText('" + text + "')");
+ }
+
+ /*
+ * @return the url ContextPath. An empty string if one does not exist.
+ */
+ public String getContext() {
+ return (request == null)? "" : request.getContextPath();
+ }
+
+ /**
+ * the selectedList objects are matched to the list.listValue
+ *
+ * listKey and listValue are optional, and if not provided, the list item is used
+ *
+ * @param selectedList the name of the action property
+ * that contains the list of selected items
+ * or single item if its not an array or list
+ * @param list the name of the action property
+ * that contains the list of selectable items
+ * @param listKey an ognl expression that is exaluated relative to the list item
+ * to use as the key of the ListEntry
+ * @param listValue an ognl expression that is exaluated relative to the list item
+ * to use as the value of the ListEntry
+ * @return a List of ListEntry
+ */
+ public List makeSelectList(String selectedList, String list, String listKey, String listValue) {
+ List selectList = new ArrayList();
+
+ Collection selectedItems = null;
+
+ Object i = stack.findValue(selectedList);
+
+ if (i != null) {
+ if (i.getClass().isArray()) {
+ selectedItems = Arrays.asList((Object[]) i);
+ } else if (i instanceof Collection) {
+ selectedItems = (Collection) i;
+ } else {
+ // treat it is a single item
+ selectedItems = new ArrayList();
+ selectedItems.add(i);
+ }
+ }
+
+ Collection items = (Collection) stack.findValue(list);
+
+ if (items != null) {
+ for (Iterator iter = items.iterator(); iter.hasNext();) {
+ Object element = (Object) iter.next();
+ Object key = null;
+
+ if ((listKey == null) || (listKey.length() == 0)) {
+ key = element;
+ } else {
+ key = ognl.findValue(listKey, element);
+ }
+
+ Object value = null;
+
+ if ((listValue == null) || (listValue.length() == 0)) {
+ value = element;
+ } else {
+ value = ognl.findValue(listValue, element);
+ }
+
+ boolean isSelected = false;
+
+ if ((value != null) && (selectedItems != null) && selectedItems.contains(value)) {
+ isSelected = true;
+ }
+
+ selectList.add(new ListEntry(key, value, isSelected));
+ }
+ }
+
+ return selectList;
+ }
+
+ public String htmlEncode(Object obj) {
+ if (obj == null) {
+ return null;
+ }
+
+ return TextUtils.htmlEncode(obj.toString());
+ }
+
+ public int toInt(long aLong) {
+ return (int) aLong;
+ }
+
+ public long toLong(int anInt) {
+ return (long) anInt;
+ }
+
+ public long toLong(String aLong) {
+ if (aLong == null) {
+ return 0;
+ }
+
+ return Long.parseLong(aLong);
+ }
+
+ public String toString(long aLong) {
+ return Long.toString(aLong);
+ }
+
+ public String toString(int anInt) {
+ return Integer.toString(anInt);
+ }
+
+
+ static class ResponseWrapper extends HttpServletResponseWrapper {
+ StringWriter strout;
+ PrintWriter writer;
+ ServletOutputStream sout;
+
+ ResponseWrapper(HttpServletResponse aResponse) {
+ super(aResponse);
+ strout = new StringWriter();
+ sout = new ServletOutputStreamWrapper(strout);
+ writer = new PrintWriter(strout);
+ }
+
+ public String getData() {
+ writer.flush();
+
+ return strout.toString();
+ }
+
+ public ServletOutputStream getOutputStream() {
+ return sout;
+ }
+
+ public PrintWriter getWriter() throws IOException {
+ return writer;
+ }
+ }
+
+ static class ServletOutputStreamWrapper extends ServletOutputStream {
+ StringWriter writer;
+
+ ServletOutputStreamWrapper(StringWriter aWriter) {
+ writer = aWriter;
+ }
+
+ public void write(int aByte) {
+ writer.write(aByte);
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/SubsetIteratorFilter.java b/trunk/core/src/main/java/org/apache/struts2/util/SubsetIteratorFilter.java
new file mode 100644
index 000000000..5909dc168
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/SubsetIteratorFilter.java
@@ -0,0 +1,173 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.opensymphony.xwork2.Action;
+
+
+/**
+ * A bean that takes an iterator and outputs a subset of it.
+ *
+ */
+public class SubsetIteratorFilter extends IteratorFilterSupport implements Iterator, Action {
+
+ private static final Log _log = LogFactory.getLog(SubsetIteratorFilter.class);
+
+ Iterator iterator;
+ Object source;
+ int count = -1;
+ int currentCount = 0;
+
+ Decider decider;
+
+ // Attributes ----------------------------------------------------
+ int start = 0;
+
+
+ public void setCount(int aCount) {
+ this.count = aCount;
+ }
+
+ // Public --------------------------------------------------------
+ public void setSource(Object anIterator) {
+ source = anIterator;
+ }
+
+ public void setStart(int aStart) {
+ this.start = aStart;
+ }
+
+ public void setDecider(Decider aDecider) {
+ this.decider = aDecider;
+ }
+
+ // Action implementation -----------------------------------------
+ public String execute() {
+ if (source == null) {
+ LogFactory.getLog(SubsetIteratorFilter.class.getName()).warn("Source is null returning empty set.");
+
+ return ERROR;
+ }
+
+ // Make source transformations
+ source = getIterator(source);
+
+ // Calculate iterator filter
+ if (source instanceof Iterator) {
+ iterator = (Iterator) source;
+
+
+ // Read away items
+ for (int i = 0; (i < start) && iterator.hasNext(); i++) {
+ iterator.next();
+ }
+
+
+ // now let Decider decide if element should be added (if a decider exist)
+ if (decider != null) {
+ List list = new ArrayList();
+ while(iterator.hasNext()) {
+ Object currentElement = iterator.next();
+ if (decide(currentElement)) {
+ list.add(currentElement);
+ }
+ }
+ iterator = list.iterator();
+ }
+
+ } else if (source.getClass().isArray()) {
+ ArrayList list = new ArrayList(((Object[]) source).length);
+ Object[] objects = (Object[]) source;
+ int len = objects.length;
+
+ if (count >= 0) {
+ len = start + count;
+ if (len > objects.length) {
+ len = objects.length;
+ }
+ }
+
+ for (int j = start; j < len; j++) {
+ if (decide(objects[j])) {
+ list.add(objects[j]);
+ }
+ }
+
+ count = -1; // Don't have to check this in the iterator code
+ iterator = list.iterator();
+ }
+
+ if (iterator == null) {
+ throw new IllegalArgumentException("Source is not an iterator:" + source);
+ }
+
+ return SUCCESS;
+ }
+
+ // Iterator implementation ---------------------------------------
+ public boolean hasNext() {
+ return (iterator == null) ? false : (iterator.hasNext() && ((count < 0) || (currentCount < count)));
+ }
+
+ public Object next() {
+ currentCount++;
+
+ return iterator.next();
+ }
+
+ public void remove() {
+ iterator.remove();
+ }
+
+ // inner class ---------------------------------------------------
+ /**
+ * A decider determines if the given element should be added to the list or not.
+ */
+ public static interface Decider {
+
+ /**
+ * Should the object be added to the list?
+ * @param element the object
+ * @return true to add.
+ * @throws Exception can be thrown.
+ */
+ boolean decide(Object element) throws Exception;
+ }
+
+ // protected -----------------------------------------------------
+ protected boolean decide(Object element) {
+ if (decider != null) {
+ try {
+ boolean okToAdd = decider.decide(element);
+ return okToAdd;
+ }
+ catch(Exception e) {
+ _log.warn("decider ["+decider+"] encountered an error while decide adding element ["+element+"], element will be ignored, it will not appeared in subseted iterator", e);
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/TabbedPane.java b/trunk/core/src/main/java/org/apache/struts2/util/TabbedPane.java
new file mode 100644
index 000000000..890bb73b1
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/TabbedPane.java
@@ -0,0 +1,65 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.Vector;
+
+
+/**
+ * A bean that helps implement a tabbed pane
+ *
+ */
+public class TabbedPane {
+
+ protected String tabAlign = null;
+
+ // Attributes ----------------------------------------------------
+ protected Vector content = null;
+ protected int selectedIndex = 0;
+
+
+ // Public --------------------------------------------------------
+ public TabbedPane(int defaultIndex) {
+ selectedIndex = defaultIndex;
+ }
+
+
+ public void setContent(Vector content) {
+ this.content = content;
+ }
+
+ public Vector getContent() {
+ return content;
+ }
+
+ public void setSelectedIndex(int selectedIndex) {
+ this.selectedIndex = selectedIndex;
+ }
+
+ public int getSelectedIndex() {
+ return selectedIndex;
+ }
+
+ public void setTabAlign(String tabAlign) {
+ this.tabAlign = tabAlign;
+ }
+
+ public String getTabAlign() {
+ return tabAlign;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/Timer.java b/trunk/core/src/main/java/org/apache/struts2/util/Timer.java
new file mode 100644
index 000000000..c06ea1879
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/Timer.java
@@ -0,0 +1,48 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+
+/**
+ * A bean that can be used to time execution of pages
+ *
+ */
+public class Timer {
+
+ // Attributes ----------------------------------------------------
+ long current = System.currentTimeMillis();
+ long start = current;
+
+
+ // Public --------------------------------------------------------
+ public long getTime() {
+ // Return how long time has passed since last check point
+ long now = System.currentTimeMillis();
+ long time = now - current;
+
+ // Reset so that next time we get from this point
+ current = now;
+
+ return time;
+ }
+
+ public long getTotal() {
+ // Reset start so that next time we get from this point
+ return System.currentTimeMillis() - start;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/TokenHelper.java b/trunk/core/src/main/java/org/apache/struts2/util/TokenHelper.java
new file mode 100644
index 000000000..8dce61b00
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/TokenHelper.java
@@ -0,0 +1,183 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.math.BigInteger;
+import java.util.Map;
+import java.util.Random;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.util.LocalizedTextUtil;
+
+/**
+ * TokenHelper
+ *
+ */
+public class TokenHelper {
+
+ /**
+ * The default name to map the token value
+ */
+ public static final String DEFAULT_TOKEN_NAME = "struts.token";
+
+ /**
+ * The name of the field which will hold the token name
+ */
+ public static final String TOKEN_NAME_FIELD = "struts.token.name";
+ private static final Log LOG = LogFactory.getLog(TokenHelper.class);
+ private static final Random RANDOM = new Random();
+
+
+ /**
+ * Sets a transaction token into the session using the default token name.
+ *
+ * @return the token string
+ */
+ public static String setToken() {
+ return setToken(DEFAULT_TOKEN_NAME);
+ }
+
+ /**
+ * Sets a transaction token into the session using the provided token name.
+ *
+ * @param tokenName the name to store into the session with the token as the value
+ * @return the token string
+ */
+ public static String setToken(String tokenName) {
+ Map session = ActionContext.getContext().getSession();
+ String token = generateGUID();
+ try {
+ session.put(tokenName, token);
+ }
+ catch(IllegalStateException e) {
+ // WW-1182 explain to user what the problem is
+ String msg = "Error creating HttpSession due response is commited to client. You can use the CreateSessionInterceptor or create the HttpSession from your action before the result is rendered to the client: " + e.getMessage();
+ LOG.error(msg, e);
+ throw new IllegalArgumentException(msg);
+ }
+
+ return token;
+ }
+
+
+ /**
+ * Gets a transaction token into the session using the default token name.
+ *
+ * @return token
+ */
+ public static String getToken() {
+ return getToken(DEFAULT_TOKEN_NAME);
+ }
+
+ /**
+ * Gets the Token value from the params in the ServletActionContext using the given name
+ *
+ * @param tokenName the name of the parameter which holds the token value
+ * @return the token String or null, if the token could not be found
+ */
+ public static String getToken(String tokenName) {
+ Map params = ActionContext.getContext().getParameters();
+ String[] tokens = (String[]) params.get(tokenName);
+ String token;
+
+ if ((tokens == null) || (tokens.length < 1)) {
+ LOG.warn("Could not find token mapped to token name " + tokenName);
+
+ return null;
+ }
+
+ token = tokens[0];
+
+ return token;
+ }
+
+ /**
+ * Gets the token name from the Parameters in the ServletActionContext
+ *
+ * @return the token name found in the params, or null if it could not be found
+ */
+ public static String getTokenName() {
+ Map params = ActionContext.getContext().getParameters();
+
+ if (!params.containsKey(TOKEN_NAME_FIELD)) {
+ LOG.warn("Could not find token name in params.");
+
+ return null;
+ }
+
+ String[] tokenNames = (String[]) params.get(TOKEN_NAME_FIELD);
+ String tokenName;
+
+ if ((tokenNames == null) || (tokenNames.length < 1)) {
+ LOG.warn("Got a null or empty token name.");
+
+ return null;
+ }
+
+ tokenName = tokenNames[0];
+
+ return tokenName;
+ }
+
+ /**
+ * Checks for a valid transaction token in the current request params. If a valid token is found, it is
+ * removed so the it is not valid again.
+ *
+ * @return false if there was no token set into the params (check by looking for {@link #TOKEN_NAME_FIELD}), true if a valid token is found
+ */
+ public static boolean validToken() {
+ String tokenName = getTokenName();
+
+ if (tokenName == null) {
+ if (LOG.isDebugEnabled())
+ LOG.debug("no token name found -> Invalid token ");
+ return false;
+ }
+
+ String token = getToken(tokenName);
+
+ if (token == null) {
+ if (LOG.isDebugEnabled())
+ LOG.debug("no token found for token name "+tokenName+" -> Invalid token ");
+ return false;
+ }
+
+ Map session = ActionContext.getContext().getSession();
+ String sessionToken = (String) session.get(tokenName);
+
+ if (!token.equals(sessionToken)) {
+ LOG.warn(LocalizedTextUtil.findText(TokenHelper.class, "struts.internal.invalid.token", ActionContext.getContext().getLocale(), "Form token {0} does not match the session token {1}.", new Object[]{
+ token, sessionToken
+ }));
+
+ return false;
+ }
+
+ // remove the token so it won't be used again
+ session.remove(tokenName);
+
+ return true;
+ }
+
+ public static String generateGUID() {
+ return new BigInteger(165, RANDOM).toString(36).toUpperCase();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/URLBean.java b/trunk/core/src/main/java/org/apache/struts2/util/URLBean.java
new file mode 100644
index 000000000..db5cd3db4
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/URLBean.java
@@ -0,0 +1,96 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.views.util.UrlHelper;
+
+
+/**
+ * A bean that can generate a URL.
+ *
+ */
+public class URLBean {
+
+ HashMap params;
+ HttpServletRequest request;
+ HttpServletResponse response;
+ String page;
+
+
+ public void setPage(String page) {
+ this.page = page;
+ }
+
+ public void setRequest(HttpServletRequest request) {
+ this.request = request;
+ }
+
+ public void setResponse(HttpServletResponse response) {
+ this.response = response;
+ }
+
+ public String getURL() {
+ // all this trickier with maps is to reduce the number of objects created
+ Map fullParams = null;
+
+ if (params != null) {
+ fullParams = new HashMap();
+ }
+
+ if (page == null) {
+ // No particular page requested, so go to "same page"
+ // Add query params to parameters
+ if (fullParams != null) {
+ fullParams.putAll(request.getParameterMap());
+ } else {
+ fullParams = request.getParameterMap();
+ }
+ }
+
+ // added parameters override, just like in URLTag
+ if (params != null) {
+ fullParams.putAll(params);
+ }
+
+ return UrlHelper.buildUrl(page, request, response, fullParams);
+ }
+
+ public URLBean addParameter(String name, Object value) {
+ if (params == null) {
+ params = new HashMap();
+ }
+
+ if (value == null) {
+ params.remove(name);
+ } else {
+ params.put(name, value.toString());
+ }
+
+ return this;
+ }
+
+ public String toString() {
+ return getURL();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/util/VelocityStrutsUtil.java b/trunk/core/src/main/java/org/apache/struts2/util/VelocityStrutsUtil.java
new file mode 100644
index 000000000..0f4f8c56d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/util/VelocityStrutsUtil.java
@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.util;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.views.velocity.VelocityManager;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * Struts velocity related util.
+ *
+ */
+public class VelocityStrutsUtil extends StrutsUtil {
+
+ private Context ctx;
+
+ public VelocityStrutsUtil(Context ctx, ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
+ super(stack, request, response);
+ this.ctx = ctx;
+ }
+
+ public String evaluate(String expression) throws IOException, ResourceNotFoundException, MethodInvocationException, ParseErrorException {
+ CharArrayWriter writer = new CharArrayWriter();
+ VelocityManager.getInstance().getVelocityEngine().evaluate(ctx, writer, "Error parsing " + expression, expression);
+
+ return writer.toString();
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/validators/DWRValidator.java b/trunk/core/src/main/java/org/apache/struts2/validators/DWRValidator.java
new file mode 100644
index 000000000..9c27d8531
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/validators/DWRValidator.java
@@ -0,0 +1,134 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.validators;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.dispatcher.ApplicationMap;
+import org.apache.struts2.dispatcher.Dispatcher;
+import org.apache.struts2.dispatcher.RequestMap;
+import org.apache.struts2.dispatcher.SessionMap;
+
+import uk.ltd.getahead.dwr.WebContextFactory;
+
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.DefaultActionInvocation;
+import com.opensymphony.xwork2.DefaultActionProxy;
+import com.opensymphony.xwork2.ValidationAware;
+import com.opensymphony.xwork2.ValidationAwareSupport;
+import com.opensymphony.xwork2.config.Configuration;
+import com.opensymphony.xwork2.config.entities.ActionConfig;
+
+/**
+ *
+ * Use the dwr configuration as follows :-
+ *
+ *
+ *
+ *
+ * <dwr<
+ * <allow<
+ * <create creator="new" javascript="validator" class="org.apache.struts2.validators.DWRValidator"/<
+ * <convert converter="bean" match="com.opensymphony.xwork2.ValidationAwareSupport"/<
+ * </allow<
+ * </dwr<
+ *
+ *
+ *
+ */
+public class DWRValidator {
+ private static final Log LOG = LogFactory.getLog(DWRValidator.class);
+
+ public ValidationAwareSupport doPost(String namespace, String action, Map params) throws Exception {
+ HttpServletRequest req = WebContextFactory.get().getHttpServletRequest();
+ ServletContext servletContext = WebContextFactory.get().getServletContext();
+ HttpServletResponse res = WebContextFactory.get().getHttpServletResponse();
+
+ Map requestParams = new HashMap(req.getParameterMap());
+ if (params != null) {
+ requestParams.putAll(params);
+ } else {
+ params = requestParams;
+ }
+ Map requestMap = new RequestMap(req);
+ Map session = new SessionMap(req);
+ Map application = new ApplicationMap(servletContext);
+ Dispatcher du = Dispatcher.getInstance();
+ HashMap ctx = du.createContextMap(requestMap,
+ params,
+ session,
+ application,
+ req,
+ res,
+ servletContext);
+
+ try {
+ Configuration cfg = du.getConfigurationManager().getConfiguration();
+ ValidatorActionProxy proxy = new ValidatorActionProxy(cfg, namespace, action, ctx);
+ proxy.execute();
+ Object a = proxy.getAction();
+
+ if (a instanceof ValidationAware) {
+ ValidationAware aware = (ValidationAware) a;
+ ValidationAwareSupport vas = new ValidationAwareSupport();
+ vas.setActionErrors(aware.getActionErrors());
+ vas.setActionMessages(aware.getActionMessages());
+ vas.setFieldErrors(aware.getFieldErrors());
+
+ return vas;
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ LOG.error("Error while trying to validate", e);
+ return null;
+ }
+ }
+
+ public static class ValidatorActionInvocation extends DefaultActionInvocation {
+ private static final long serialVersionUID = -7645433725470191275L;
+
+ protected ValidatorActionInvocation(ActionProxy proxy, Map extraContext) throws Exception {
+ super(proxy, extraContext, true);
+ }
+
+ protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
+ return Action.NONE; // don't actually execute the action
+ }
+ }
+
+ public static class ValidatorActionProxy extends DefaultActionProxy {
+ private static final long serialVersionUID = 5754781916414047963L;
+
+ protected ValidatorActionProxy(Configuration config, String namespace, String actionName, Map extraContext) throws Exception {
+ super(config, namespace, actionName, extraContext, false, true);
+ }
+
+ protected void prepare() throws Exception {
+ invocation = new ValidatorActionInvocation(this, extraContext);
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/JspSupportServlet.java b/trunk/core/src/main/java/org/apache/struts2/views/JspSupportServlet.java
new file mode 100644
index 000000000..8fde4da96
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/JspSupportServlet.java
@@ -0,0 +1,37 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+
+/**
+ */
+public class JspSupportServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 8302309812391541933L;
+
+ public static JspSupportServlet jspSupportServlet;
+
+ public void init(ServletConfig servletConfig) throws ServletException {
+ super.init(servletConfig);
+
+ jspSupportServlet = this;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerManager.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerManager.java
new file mode 100644
index 000000000..0a3c8d88a
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerManager.java
@@ -0,0 +1,342 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.servlet.GenericServlet;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.config.Settings;
+import org.apache.struts2.views.JspSupportServlet;
+import org.apache.struts2.views.freemarker.tags.StrutsModels;
+import org.apache.struts2.views.util.ContextUtil;
+
+import com.opensymphony.xwork2.util.FileManager;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.ObjectFactory;
+
+import freemarker.cache.FileTemplateLoader;
+import freemarker.cache.MultiTemplateLoader;
+import freemarker.cache.TemplateLoader;
+import freemarker.cache.WebappTemplateLoader;
+import freemarker.ext.beans.BeansWrapper;
+import freemarker.ext.jsp.TaglibFactory;
+import freemarker.ext.servlet.HttpRequestHashModel;
+import freemarker.ext.servlet.HttpRequestParametersHashModel;
+import freemarker.ext.servlet.HttpSessionHashModel;
+import freemarker.ext.servlet.ServletContextHashModel;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.SimpleHash;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateExceptionHandler;
+import freemarker.template.TemplateModel;
+
+
+/**
+ * Static Configuration Manager for the FreemarkerResult's configuration
+ *
+ *
+ *
+ * Possible extension points are :-
+ *
+ * createConfiguration method
+ * loadSettings method
+ * getTemplateLoader method
+ * populateContext method
+ *
+ *
+ *
+ * createConfiguration method
+ * Create a freemarker Configuration.
+ *
+ *
+ * loadSettings method
+ * Load freemarker settings, default to freemarker.properties (if found in classpath)
+ *
+ *
+ * getTemplateLoader method
+ * create a freemarker TemplateLoader that loads freemarker template in the following order :-
+ *
+ * path defined in ServletContext init parameter named 'templatePath' or 'TemplatePath' (must be an absolute path)
+ * webapp classpath
+ * struts's static folder (under [STRUT2_SOURCE]/org/apache/struts2/static/
+ *
+ *
+ *
+ * populateContext method
+ * populate the created model.
+ *
+ */
+public class FreemarkerManager {
+
+ private static final Log log = LogFactory.getLog(FreemarkerManager.class);
+ public static final String CONFIG_SERVLET_CONTEXT_KEY = "freemarker.Configuration";
+ public static final String KEY_EXCEPTION = "exception";
+
+ // coppied from freemarker servlet - since they are private
+ private static final String ATTR_APPLICATION_MODEL = ".freemarker.Application";
+ private static final String ATTR_JSP_TAGLIBS_MODEL = ".freemarker.JspTaglibs";
+ private static final String ATTR_REQUEST_MODEL = ".freemarker.Request";
+ private static final String ATTR_REQUEST_PARAMETERS_MODEL = ".freemarker.RequestParameters";
+
+ // coppied from freemarker servlet - so that there is no dependency on it
+ public static final String KEY_APPLICATION = "Application";
+ public static final String KEY_REQUEST_MODEL = "Request";
+ public static final String KEY_SESSION_MODEL = "Session";
+ public static final String KEY_JSP_TAGLIBS = "JspTaglibs";
+ public static final String KEY_REQUEST_PARAMETER_MODEL = "Parameters";
+ private static FreemarkerManager instance = null;
+
+
+ /**
+ * To allow for custom configuration of freemarker, sublcass this class "ConfigManager" and
+ * set the Struts configuration property
+ * struts.freemarker.configmanager.classname to the fully qualified classname.
+ *
+ * This allows you to override the protected methods in the ConfigMangaer
+ * to programatically create your own Configuration instance
+ */
+ public final static synchronized FreemarkerManager getInstance() {
+ if (instance == null) {
+ String classname = FreemarkerManager.class.getName();
+
+ if (Settings.isSet(StrutsConstants.STRUTS_FREEMARKER_MANAGER_CLASSNAME)) {
+ classname = Settings.get(StrutsConstants.STRUTS_FREEMARKER_MANAGER_CLASSNAME).trim();
+ }
+
+ try {
+ log.info("Instantiating Freemarker ConfigManager!, " + classname);
+ // singleton instances shouldn't be built accessing request or session-specific context data
+ instance = (FreemarkerManager) ObjectFactory.getObjectFactory().buildBean(classname, null);
+ } catch (Exception e) {
+ log.fatal("Fatal exception occurred while trying to instantiate a Freemarker ConfigManager instance, " + classname, e);
+ }
+ }
+
+ // if the instance creation failed, make sure there is a default instance
+ if (instance == null) {
+ instance = new FreemarkerManager();
+ }
+
+ return instance;
+ }
+
+ public final synchronized freemarker.template.Configuration getConfiguration(ServletContext servletContext) throws TemplateException {
+ freemarker.template.Configuration config = (freemarker.template.Configuration) servletContext.getAttribute(CONFIG_SERVLET_CONTEXT_KEY);
+
+ if (config == null) {
+ config = createConfiguration(servletContext);
+
+ // store this configuration in the servlet context
+ servletContext.setAttribute(CONFIG_SERVLET_CONTEXT_KEY, config);
+ }
+
+ config.setWhitespaceStripping(true);
+
+ return config;
+ }
+
+ protected ScopesHashModel buildScopesHashModel(ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, ObjectWrapper wrapper, ValueStack stack) {
+ ScopesHashModel model = new ScopesHashModel(wrapper, servletContext, request, stack);
+
+ // Create hash model wrapper for servlet context (the application)
+ // only need one thread to do this once, per servlet context
+ synchronized (servletContext) {
+ ServletContextHashModel servletContextModel = (ServletContextHashModel) servletContext.getAttribute(ATTR_APPLICATION_MODEL);
+
+ if (servletContextModel == null) {
+
+ GenericServlet servlet = JspSupportServlet.jspSupportServlet;
+ // TODO if the jsp support servlet isn't load-on-startup then it won't exist
+ // if it hasn't been accessed, and a JSP page is accessed
+ if (servlet != null) {
+ servletContextModel = new ServletContextHashModel(servlet, wrapper);
+ servletContext.setAttribute(ATTR_APPLICATION_MODEL, servletContextModel);
+ TaglibFactory taglibs = new TaglibFactory(servletContext);
+ servletContext.setAttribute(ATTR_JSP_TAGLIBS_MODEL, taglibs);
+ }
+
+ }
+
+ model.put(KEY_APPLICATION, servletContextModel);
+ model.put(KEY_JSP_TAGLIBS, (TemplateModel) servletContext.getAttribute(ATTR_JSP_TAGLIBS_MODEL));
+ }
+
+ // Create hash model wrapper for session
+ HttpSession session = request.getSession(false);
+ if (session != null) {
+ model.put(KEY_SESSION_MODEL, new HttpSessionHashModel(session, wrapper));
+ } else {
+ // no session means no attributes ???
+ // model.put(KEY_SESSION_MODEL, new SimpleHash());
+ }
+
+ // Create hash model wrapper for the request attributes
+ HttpRequestHashModel requestModel = (HttpRequestHashModel) request.getAttribute(ATTR_REQUEST_MODEL);
+
+ if ((requestModel == null) || (requestModel.getRequest() != request)) {
+ requestModel = new HttpRequestHashModel(request, response, wrapper);
+ request.setAttribute(ATTR_REQUEST_MODEL, requestModel);
+ }
+
+ model.put(KEY_REQUEST_MODEL, requestModel);
+
+
+ // Create hash model wrapper for request parameters
+ HttpRequestParametersHashModel reqParametersModel = (HttpRequestParametersHashModel) request.getAttribute(ATTR_REQUEST_PARAMETERS_MODEL);
+ if (reqParametersModel == null || requestModel.getRequest() != request) {
+ reqParametersModel = new HttpRequestParametersHashModel(request);
+ request.setAttribute(ATTR_REQUEST_PARAMETERS_MODEL, reqParametersModel);
+ }
+ model.put(KEY_REQUEST_PARAMETER_MODEL, reqParametersModel);
+
+ return model;
+ }
+
+ protected void populateContext(ScopesHashModel model, ValueStack stack, Object action, HttpServletRequest request, HttpServletResponse response) {
+ // put the same objects into the context that the velocity result uses
+ Map standard = ContextUtil.getStandardContext(stack, request, response);
+ model.putAll(standard);
+
+ // support for JSP exception pages, exposing the servlet or JSP exception
+ Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception");
+
+ if (exception == null) {
+ exception = (Throwable) request.getAttribute("javax.servlet.error.JspException");
+ }
+
+ if (exception != null) {
+ model.put(KEY_EXCEPTION, exception);
+ }
+ }
+
+ protected BeansWrapper getObjectWrapper() {
+ return new StrutsBeanWrapper();
+ }
+
+ /**
+ * The default template loader is a MultiTemplateLoader which includes
+ * a ClassTemplateLoader and a WebappTemplateLoader (and a FileTemplateLoader depending on
+ * the init-parameter 'TemplatePath').
+ *
+ * The ClassTemplateLoader will resolve fully qualified template includes
+ * that begin with a slash. for example /com/company/template/common.ftl
+ *
+ * The WebappTemplateLoader attempts to resolve templates relative to the web root folder
+ */
+ protected TemplateLoader getTemplateLoader(ServletContext servletContext) {
+ // construct a FileTemplateLoader for the init-param 'TemplatePath'
+ FileTemplateLoader templatePathLoader = null;
+
+ String templatePath = servletContext.getInitParameter("TemplatePath");
+ if (templatePath == null) {
+ templatePath = servletContext.getInitParameter("templatePath");
+ }
+
+ if (templatePath != null) {
+ try {
+ templatePathLoader = new FileTemplateLoader(new File(templatePath));
+ } catch (IOException e) {
+ log.error("Invalid template path specified: " + e.getMessage(), e);
+ }
+ }
+
+ // presume that most apps will require the class and webapp template loader
+ // if people wish to
+ return templatePathLoader != null ?
+ new MultiTemplateLoader(new TemplateLoader[]{
+ templatePathLoader,
+ new WebappTemplateLoader(servletContext),
+ new StrutsClassTemplateLoader()
+ })
+ : new MultiTemplateLoader(new TemplateLoader[]{
+ new WebappTemplateLoader(servletContext),
+ new StrutsClassTemplateLoader()
+ });
+ }
+
+ /**
+ * Create the instance of the freemarker Configuration object.
+ *
+ * this implementation
+ *
+ * obtains the default configuration from Configuration.getDefaultConfiguration()
+ * sets up template loading from a ClassTemplateLoader and a WebappTemplateLoader
+ * sets up the object wrapper to be the BeansWrapper
+ * loads settings from the classpath file /freemarker.properties
+ *
+ *
+ * @param servletContext
+ */
+ protected freemarker.template.Configuration createConfiguration(ServletContext servletContext) throws TemplateException {
+ freemarker.template.Configuration configuration = new freemarker.template.Configuration();
+
+ configuration.setTemplateLoader(getTemplateLoader(servletContext));
+
+ configuration.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
+
+ configuration.setObjectWrapper(getObjectWrapper());
+
+ if (Settings.isSet(StrutsConstants.STRUTS_I18N_ENCODING)) {
+ configuration.setDefaultEncoding(Settings.get(StrutsConstants.STRUTS_I18N_ENCODING));
+ }
+
+ loadSettings(servletContext, configuration);
+
+ return configuration;
+ }
+
+ /**
+ * Load the settings from the /freemarker.properties file on the classpath
+ *
+ * @see freemarker.template.Configuration#setSettings for the definition of valid settings
+ */
+ protected void loadSettings(ServletContext servletContext, freemarker.template.Configuration configuration) {
+ try {
+ InputStream in = FileManager.loadFile("freemarker.properties", FreemarkerManager.class);
+
+ if (in != null) {
+ Properties p = new Properties();
+ p.load(in);
+ configuration.setSettings(p);
+ }
+ } catch (IOException e) {
+ log.error("Error while loading freemarker settings from /freemarker.properties", e);
+ } catch (TemplateException e) {
+ log.error("Error while loading freemarker settings from /freemarker.properties", e);
+ }
+ }
+
+ public SimpleHash buildTemplateModel(ValueStack stack, Object action, ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, ObjectWrapper wrapper) {
+ ScopesHashModel model = buildScopesHashModel(servletContext, request, response, wrapper, stack);
+ populateContext(model, stack, action, request, response);
+ model.put("s", new StrutsModels(stack, request, response));
+ return model;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerResult.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerResult.java
new file mode 100644
index 000000000..ac6105df0
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerResult.java
@@ -0,0 +1,280 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Locale;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.dispatcher.StrutsResultSupport;
+import org.apache.struts2.views.util.ResourceUtil;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.LocaleProvider;
+import com.opensymphony.xwork2.util.ValueStack;
+
+import freemarker.template.Configuration;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+
+/**
+ *
+ *
+ * Renders a view using the Freemarker template engine.
+ *
+ * The FreemarkarManager class configures the template loaders so that the
+ * template location can be either
+ *
+ *
+ *
+ *
+ * relative to the web root folder. eg /WEB-INF/views/home.ftl
+ *
+ *
+ * a classpath resuorce. eg com/company/web/views/home.ftl
+ *
+ *
+ *
+ *
+ *
+ * This result type takes the following parameters:
+ *
+ *
+ *
+ *
+ *
+ * location (default) - the location of the template to process.
+ *
+ * parse - true by default. If set to false, the location param will
+ * not be parsed for Ognl expressions.
+ *
+ * contentType - defaults to "text/html" unless specified.
+ *
+ *
+ *
+ *
+ *
+ * Example:
+ *
+ *
+ *
+ *
+ * <result name="success" type="freemarker">foo.ftl</result>
+ *
+ *
+ *
+ */
+public class FreemarkerResult extends StrutsResultSupport {
+
+ private static final long serialVersionUID = -3778230771704661631L;
+
+ protected ActionInvocation invocation;
+ protected Configuration configuration;
+ protected ObjectWrapper wrapper;
+
+ /*
+ * Struts results are constructed for each result execution
+ *
+ * the current context is availible to subclasses via these protected fields
+ */
+ protected String location;
+ private String pContentType = "text/html";
+
+ public FreemarkerResult() {
+ super();
+ }
+
+ public FreemarkerResult(String location) {
+ super(location);
+ }
+
+ public void setContentType(String aContentType) {
+ pContentType = aContentType;
+ }
+
+ /**
+ * allow parameterization of the contentType
+ * the default being text/html
+ */
+ public String getContentType() {
+ return pContentType;
+ }
+
+ /**
+ * Execute this result, using the specified template location.
+ *
+ * The template location has already been interoplated for any variable substitutions
+ *
+ * this method obtains the freemarker configuration and the object wrapper from the provided hooks.
+ * It them implements the template processing workflow by calling the hooks for
+ * preTemplateProcess and postTemplateProcess
+ */
+ public void doExecute(String location, ActionInvocation invocation) throws IOException, TemplateException {
+ this.location = location;
+ this.invocation = invocation;
+ this.configuration = getConfiguration();
+ this.wrapper = getObjectWrapper();
+
+ if (!location.startsWith("/")) {
+ ActionContext ctx = invocation.getInvocationContext();
+ HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
+ String base = ResourceUtil.getResourceBase(req);
+ location = base + "/" + location;
+ }
+
+ Template template = configuration.getTemplate(location, deduceLocale());
+ TemplateModel model = createModel();
+
+ // Give subclasses a chance to hook into preprocessing
+ if (preTemplateProcess(template, model)) {
+ try {
+ // Process the template
+ template.process(model, getWriter());
+ } finally {
+ // Give subclasses a chance to hook into postprocessing
+ postTemplateProcess(template, model);
+ }
+ }
+ }
+
+ /**
+ * This method is called from {@link #doExecute(String, ActionInvocation)} to obtain the
+ * FreeMarker configuration object that this result will use for template loading. This is a
+ * hook that allows you to custom-configure the configuration object in a subclass, or to fetch
+ * it from an IoC container.
+ *
+ *
+ * The default implementation obtains the configuration from the ConfigurationManager instance.
+ *
+ */
+ protected Configuration getConfiguration() throws TemplateException {
+ return FreemarkerManager.getInstance().getConfiguration(ServletActionContext.getServletContext());
+ }
+
+ /**
+ * This method is called from {@link #doExecute(String, ActionInvocation)} to obtain the
+ * FreeMarker object wrapper object that this result will use for adapting objects into template
+ * models. This is a hook that allows you to custom-configure the wrapper object in a subclass.
+ *
+ *
+ * The default implementation returns {@link Configuration#getObjectWrapper()}
+ *
+ */
+ protected ObjectWrapper getObjectWrapper() {
+ return configuration.getObjectWrapper();
+ }
+
+ /**
+ * The default writer writes directly to the response writer.
+ */
+ protected Writer getWriter() throws IOException {
+ return ServletActionContext.getResponse().getWriter();
+ }
+
+ /**
+ * Build the instance of the ScopesHashModel, including JspTagLib support
+ *
+ * Objects added to the model are
+ *
+ *
+ * Application - servlet context attributes hash model
+ * JspTaglibs - jsp tag lib factory model
+ * Request - request attributes hash model
+ * Session - session attributes hash model
+ * request - the HttpServletRequst object for direct access
+ * response - the HttpServletResponse object for direct access
+ * stack - the OgnLValueStack instance for direct access
+ * ognl - the instance of the OgnlTool
+ * action - the action itself
+ * exception - optional : the JSP or Servlet exception as per the servlet spec (for JSP Exception pages)
+ * struts - instance of the StrutsUtil class
+ *
+ */
+ protected TemplateModel createModel() throws TemplateModelException {
+ ServletContext servletContext = ServletActionContext.getServletContext();
+ HttpServletRequest request = ServletActionContext.getRequest();
+ HttpServletResponse response = ServletActionContext.getResponse();
+ ValueStack stack = ServletActionContext.getContext().getValueStack();
+
+ Object action = null;
+ if(invocation!= null ) action = invocation.getAction(); //Added for NullPointException
+ return FreemarkerManager.getInstance().buildTemplateModel(stack, action, servletContext, request, response, wrapper);
+ }
+
+ /**
+ * Returns the locale used for the {@link Configuration#getTemplate(String, Locale)} call. The base implementation
+ * simply returns the locale setting of the action (assuming the action implements {@link LocaleProvider}) or, if
+ * the action does not the configuration's locale is returned. Override this method to provide different behaviour,
+ */
+ protected Locale deduceLocale() {
+ if (invocation.getAction() instanceof LocaleProvider) {
+ return ((LocaleProvider) invocation.getAction()).getLocale();
+ } else {
+ return configuration.getLocale();
+ }
+ }
+
+ /**
+ * the default implementation of postTemplateProcess applies the contentType parameter
+ */
+ protected void postTemplateProcess(Template template, TemplateModel data) throws IOException {
+ }
+
+ /**
+ * Called before the execution is passed to template.process().
+ * This is a generic hook you might use in subclasses to perform a specific
+ * action before the template is processed. By default does nothing.
+ * A typical action to perform here is to inject application-specific
+ * objects into the model root
+ *
+ * @return true to process the template, false to suppress template processing.
+ */
+ protected boolean preTemplateProcess(Template template, TemplateModel model) throws IOException {
+ Object attrContentType = template.getCustomAttribute("content_type");
+
+ if (attrContentType != null) {
+ ServletActionContext.getResponse().setContentType(attrContentType.toString());
+ } else {
+ String contentType = getContentType();
+
+ if (contentType == null) {
+ contentType = "text/html";
+ }
+
+ String encoding = template.getEncoding();
+
+ if (encoding != null) {
+ contentType = contentType + "; charset=" + encoding;
+ }
+
+ ServletActionContext.getResponse().setContentType(contentType);
+ }
+
+ return true;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/PortletFreemarkerResult.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/PortletFreemarkerResult.java
new file mode 100644
index 000000000..b5d3c5aa9
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/PortletFreemarkerResult.java
@@ -0,0 +1,283 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Locale;
+
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletException;
+import javax.portlet.PortletRequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.dispatcher.StrutsResultSupport;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.context.PortletActionContext;
+import org.apache.struts2.views.util.ResourceUtil;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.util.ValueStack;
+
+import freemarker.template.Configuration;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+/**
+ */
+public class PortletFreemarkerResult extends StrutsResultSupport {
+
+ private static final long serialVersionUID = -5570612389289887543L;
+
+ protected ActionInvocation invocation;
+
+ protected Configuration configuration;
+
+ protected ObjectWrapper wrapper;
+
+ /*
+ * Struts results are constructed for each result execeution
+ *
+ * the current context is availible to subclasses via these protected fields
+ */
+ protected String location;
+
+ private String pContentType = "text/html";
+
+ public PortletFreemarkerResult() {
+ super();
+ }
+
+ public PortletFreemarkerResult(String location) {
+ super(location);
+ }
+
+ public void setContentType(String aContentType) {
+ pContentType = aContentType;
+ }
+
+ /**
+ * allow parameterization of the contentType the default being text/html
+ */
+ public String getContentType() {
+ return pContentType;
+ }
+
+ /**
+ * Execute this result, using the specified template location.
The
+ * template location has already been interoplated for any variable
+ * substitutions
this method obtains the freemarker configuration and
+ * the object wrapper from the provided hooks. It them implements the
+ * template processing workflow by calling the hooks for preTemplateProcess
+ * and postTemplateProcess
+ */
+ public void doExecute(String location, ActionInvocation invocation)
+ throws IOException, TemplateException, PortletException {
+ if (PortletActionContext.isEvent()) {
+ executeActionResult(location, invocation);
+ } else if (PortletActionContext.isRender()) {
+ executeRenderResult(location, invocation);
+ }
+ }
+
+ /**
+ * @param location
+ * @param invocation
+ */
+ private void executeActionResult(String location,
+ ActionInvocation invocation) {
+ ActionResponse res = PortletActionContext.getActionResponse();
+ // View is rendered outside an action...uh oh...
+ res.setRenderParameter(PortletActionConstants.ACTION_PARAM, "freemarkerDirect");
+ res.setRenderParameter("location", location);
+ res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext
+ .getRequest().getPortletMode().toString());
+
+ }
+
+ /**
+ * @param location
+ * @param invocation
+ * @throws TemplateException
+ * @throws IOException
+ * @throws TemplateModelException
+ */
+ private void executeRenderResult(String location,
+ ActionInvocation invocation) throws TemplateException, IOException,
+ TemplateModelException, PortletException {
+ prepareServletActionContext();
+ this.location = location;
+ this.invocation = invocation;
+ this.configuration = getConfiguration();
+ this.wrapper = getObjectWrapper();
+
+ HttpServletRequest req = ServletActionContext.getRequest();
+
+ if (!location.startsWith("/")) {
+ String base = ResourceUtil.getResourceBase(req);
+ location = base + "/" + location;
+ }
+
+ Template template = configuration.getTemplate(location, deduceLocale());
+ TemplateModel model = createModel();
+ // Give subclasses a chance to hook into preprocessing
+ if (preTemplateProcess(template, model)) {
+ try {
+ // Process the template
+ PortletActionContext.getRenderResponse().setContentType(pContentType);
+ template.process(model, getWriter());
+ } finally {
+ // Give subclasses a chance to hook into postprocessing
+ postTemplateProcess(template, model);
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private void prepareServletActionContext() throws PortletException,
+ IOException {
+ PortletRequestDispatcher disp = PortletActionContext.getPortletConfig()
+ .getPortletContext().getNamedDispatcher("preparator");
+ disp.include(PortletActionContext.getRenderRequest(),
+ PortletActionContext.getRenderResponse());
+ }
+
+ /**
+ * This method is called from {@link #doExecute(String, ActionInvocation)}
+ * to obtain the FreeMarker configuration object that this result will use
+ * for template loading. This is a hook that allows you to custom-configure
+ * the configuration object in a subclass, or to fetch it from an IoC
+ * container.
The default implementation obtains the configuration
+ * from the ConfigurationManager instance.
+ */
+ protected Configuration getConfiguration() throws TemplateException {
+ return FreemarkerManager.getInstance().getConfiguration(
+ ServletActionContext.getServletContext());
+ }
+
+ /**
+ * This method is called from {@link #doExecute(String, ActionInvocation)}
+ * to obtain the FreeMarker object wrapper object that this result will use
+ * for adapting objects into template models. This is a hook that allows you
+ * to custom-configure the wrapper object in a subclass.
The default
+ * implementation returns {@link Configuration#getObjectWrapper()}
+ */
+ protected ObjectWrapper getObjectWrapper() {
+ return configuration.getObjectWrapper();
+ }
+
+ /**
+ * The default writer writes directly to the response writer.
+ */
+ protected Writer getWriter() throws IOException {
+ return PortletActionContext.getRenderResponse().getWriter();
+ }
+
+ /**
+ * Build the instance of the ScopesHashModel, including JspTagLib support
+ *
Objects added to the model are
+ *
+ * Application - servlet context attributes hash model
+ * JspTaglibs - jsp tag lib factory model
+ * Request - request attributes hash model
+ * Session - session attributes hash model
+ * request - the HttpServletRequst object for direct access
+ * response - the HttpServletResponse object for direct access
+ * stack - the OgnLValueStack instance for direct access
+ * ognl - the instance of the OgnlTool
+ * action - the action itself
+ * exception - optional : the JSP or Servlet exception as per the
+ * servlet spec (for JSP Exception pages)
+ * struts - instance of the StrutsUtil class
+ *
+ */
+ protected TemplateModel createModel() throws TemplateModelException {
+ ServletContext servletContext = ServletActionContext
+ .getServletContext();
+ HttpServletRequest request = ServletActionContext.getRequest();
+ HttpServletResponse response = ServletActionContext.getResponse();
+ ValueStack stack = ServletActionContext.getContext()
+ .getValueStack();
+ return FreemarkerManager.getInstance().buildTemplateModel(stack,
+ invocation.getAction(), servletContext, request, response,
+ wrapper);
+ }
+
+ /**
+ * Returns the locale used for the
+ * {@link Configuration#getTemplate(String, Locale)}call. The base
+ * implementation simply returns the locale setting of the configuration.
+ * Override this method to provide different behaviour,
+ */
+ protected Locale deduceLocale() {
+ return configuration.getLocale();
+ }
+
+ /**
+ * the default implementation of postTemplateProcess applies the contentType
+ * parameter
+ */
+ protected void postTemplateProcess(Template template, TemplateModel data)
+ throws IOException {
+ }
+
+ /**
+ * Called before the execution is passed to template.process(). This is a
+ * generic hook you might use in subclasses to perform a specific action
+ * before the template is processed. By default does nothing. A typical
+ * action to perform here is to inject application-specific objects into the
+ * model root
+ *
+ * @return true to process the template, false to suppress template
+ * processing.
+ */
+ protected boolean preTemplateProcess(Template template, TemplateModel model)
+ throws IOException {
+ Object attrContentType = template.getCustomAttribute("content_type");
+
+ if (attrContentType != null) {
+ ServletActionContext.getResponse().setContentType(
+ attrContentType.toString());
+ } else {
+ String contentType = getContentType();
+
+ if (contentType == null) {
+ contentType = "text/html";
+ }
+
+ String encoding = template.getEncoding();
+
+ if (encoding != null) {
+ contentType = contentType + "; charset=" + encoding;
+ }
+
+ ServletActionContext.getResponse().setContentType(contentType);
+ }
+
+ return true;
+ }
+}
+
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/ScopesHashModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/ScopesHashModel.java
new file mode 100644
index 000000000..b8387d55c
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/ScopesHashModel.java
@@ -0,0 +1,118 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+import freemarker.template.ObjectWrapper;
+import freemarker.template.SimpleHash;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+
+/**
+ * Simple Hash model that also searches other scopes.
+ *
+ * If the key doesn't exist in this hash, this template model tries to
+ * resolve the key within the attributes of the following scopes,
+ * in the order stated: Request, Session, Servlet Context
+ */
+public class ScopesHashModel extends SimpleHash {
+
+ private static final long serialVersionUID = 5551686380141886764L;
+
+ private HttpServletRequest request;
+ private ServletContext servletContext;
+ private ValueStack stack;
+
+
+ public ScopesHashModel(ObjectWrapper objectWrapper, ServletContext context, HttpServletRequest request, ValueStack stack) {
+ super(objectWrapper);
+ this.servletContext = context;
+ this.request = request;
+ this.stack = stack;
+ }
+
+
+ public TemplateModel get(String key) throws TemplateModelException {
+ // Lookup in default scope
+ TemplateModel model = super.get(key);
+
+ if (model != null) {
+ return model;
+ }
+
+
+ if (stack != null) {
+ Object obj = stack.findValue(key);
+
+ if (obj != null) {
+ return wrap(obj);
+ }
+
+ // ok, then try the context
+ obj = stack.getContext().get(key);
+ if (obj != null) {
+ return wrap(obj);
+ }
+ }
+
+ if (request != null) {
+ // Lookup in request scope
+ Object obj = request.getAttribute(key);
+
+ if (obj != null) {
+ return wrap(obj);
+ }
+
+ // Lookup in session scope
+ HttpSession session = request.getSession(false);
+
+ if (session != null) {
+ obj = session.getAttribute(key);
+
+ if (obj != null) {
+ return wrap(obj);
+ }
+ }
+ }
+
+ if (servletContext != null) {
+ // Lookup in application scope
+ Object obj = servletContext.getAttribute(key);
+
+ if (obj != null) {
+ return wrap(obj);
+ }
+ }
+
+ return null;
+ }
+
+ public void put(String string, boolean b) {
+ super.put(string, b);
+ }
+
+ public void put(String string, Object object) {
+ super.put(string, object);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/StrutsBeanWrapper.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/StrutsBeanWrapper.java
new file mode 100644
index 000000000..1cf4284a4
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/StrutsBeanWrapper.java
@@ -0,0 +1,95 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker;
+
+import java.util.Map;
+import java.util.Set;
+
+import freemarker.core.CollectionAndSequence;
+import freemarker.ext.beans.BeansWrapper;
+import freemarker.ext.beans.MapModel;
+import freemarker.ext.util.ModelFactory;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.SimpleSequence;
+import freemarker.template.TemplateBooleanModel;
+import freemarker.template.TemplateCollectionModel;
+import freemarker.template.TemplateHashModelEx;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+/**
+ *
+ *
+ * The StrutsBeanWrapper extends the default FreeMarker BeansWrapper and provides almost no change in functionality,
+ * except for how it handles maps. Normally, FreeMarker has two modes of operation: either support for friendly
+ * map built-ins (?keys, ?values, etc) but only support for String keys; OR no special built-in support (ie: ?keys
+ * returns the methods on the map instead of the keys) but support for String and non-String keys alike. Struts
+ * provides an alternative implementation that gives us the best of both worlds.
+ *
+ *
It is possible that this special behavior may be confusing or can cause problems. Therefore, you can set the
+ * struts.freemarker.wrapper.altMap property in struts.properties to false, allowing the normal BeansWrapper
+ * logic to take place instead.
+ *
+ *
+ */
+public class StrutsBeanWrapper extends BeansWrapper {
+ private static final boolean altMapWrapper
+ = "true".equals(org.apache.struts2.config.Settings.get("struts.freemarker.wrapper.altMap"));
+
+ public TemplateModel wrap(Object object) throws TemplateModelException {
+ if (object instanceof TemplateBooleanModel) {
+ return super.wrap(object);
+ }
+
+ // attempt to get the best of both the SimpleMapModel and the MapModel of FM.
+ if (altMapWrapper && object instanceof Map) {
+ return getInstance(object, FriendlyMapModel.FACTORY);
+ }
+
+ return super.wrap(object);
+ }
+
+ /**
+ * Attempting to get the best of both worlds of FM's MapModel and SimpleMapModel, by reimplementing the isEmpty(),
+ * keySet() and values() methods. ?keys and ?values built-ins are thus available, just as well as plain Map
+ * methods.
+ */
+ private final static class FriendlyMapModel extends MapModel implements TemplateHashModelEx {
+ static final ModelFactory FACTORY = new ModelFactory() {
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new FriendlyMapModel((Map) object, (BeansWrapper) wrapper);
+ }
+ };
+
+ public FriendlyMapModel(Map map, BeansWrapper wrapper) {
+ super(map, wrapper);
+ }
+
+ public boolean isEmpty() {
+ return ((Map) object).isEmpty();
+ }
+
+ protected Set keySet() {
+ return ((Map) object).keySet();
+ }
+
+ public TemplateCollectionModel values() {
+ return new CollectionAndSequence(new SimpleSequence(((Map) object).values(), wrapper));
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/StrutsClassTemplateLoader.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/StrutsClassTemplateLoader.java
new file mode 100644
index 000000000..cda5b46e9
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/StrutsClassTemplateLoader.java
@@ -0,0 +1,32 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker;
+
+import java.net.URL;
+
+import com.opensymphony.xwork2.util.ClassLoaderUtil;
+
+import freemarker.cache.URLTemplateLoader;
+
+/**
+ */
+public class StrutsClassTemplateLoader extends URLTemplateLoader {
+ protected URL getURL(String name) {
+ return ClassLoaderUtil.getResource(name, getClass());
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ActionErrorModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ActionErrorModel.java
new file mode 100644
index 000000000..9e66613b4
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ActionErrorModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ActionError;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ActionError
+ */
+public class ActionErrorModel extends TagModel {
+ public ActionErrorModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new ActionError(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ActionMessageModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ActionMessageModel.java
new file mode 100644
index 000000000..a3c7508c6
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ActionMessageModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ActionMessage;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ActionMessage
+ */
+public class ActionMessageModel extends TagModel {
+ public ActionMessageModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new ActionMessage(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ActionModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ActionModel.java
new file mode 100644
index 000000000..b057657f5
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ActionModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ActionComponent;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ActionComponent
+ */
+public class ActionModel extends TagModel {
+ public ActionModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new ActionComponent(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/AnchorModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/AnchorModel.java
new file mode 100644
index 000000000..bb529ad80
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/AnchorModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Anchor;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Anchor
+ */
+public class AnchorModel extends TagModel {
+ public AnchorModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Anchor(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/BeanModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/BeanModel.java
new file mode 100644
index 000000000..d3e7a2678
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/BeanModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Bean;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Bean
+ */
+public class BeanModel extends TagModel {
+ public BeanModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Bean(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/CallbackWriter.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/CallbackWriter.java
new file mode 100644
index 000000000..cedd7b6b0
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/CallbackWriter.java
@@ -0,0 +1,96 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import org.apache.struts2.components.Component;
+
+import freemarker.template.TemplateModelException;
+import freemarker.template.TransformControl;
+
+/**
+ */
+public class CallbackWriter extends Writer implements TransformControl {
+ private Component bean;
+ private Writer writer;
+ private StringWriter body;
+ private boolean afterBody = false;
+
+ public CallbackWriter(Component bean, Writer writer) {
+ this.bean = bean;
+ this.writer = writer;
+
+ if (bean.usesBody()) {
+ this.body = new StringWriter();
+ }
+ }
+
+ public void close() throws IOException {
+ if (bean.usesBody()) {
+ body.close();
+ }
+ }
+
+ public void flush() throws IOException {
+ writer.flush();
+
+ if (bean.usesBody()) {
+ body.flush();
+ }
+ }
+
+ public void write(char cbuf[], int off, int len) throws IOException {
+ if (bean.usesBody() && !afterBody) {
+ body.write(cbuf, off, len);
+ } else {
+ writer.write(cbuf, off, len);
+ }
+ }
+
+ public int onStart() throws TemplateModelException, IOException {
+ boolean result = bean.start(this);
+
+ if (result) {
+ return EVALUATE_BODY;
+ } else {
+ return SKIP_BODY;
+ }
+ }
+
+ public int afterBody() throws TemplateModelException, IOException {
+ afterBody = true;
+ boolean result = bean.end(this, bean.usesBody() ? body.toString() : "");
+
+ if (result) {
+ return REPEAT_EVALUATION;
+ } else {
+ return END_EVALUATION;
+ }
+ }
+
+ public void onError(Throwable throwable) throws Throwable {
+ throw throwable;
+ }
+
+ public Component getBean() {
+ return bean;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/CheckboxListModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/CheckboxListModel.java
new file mode 100644
index 000000000..fb9e28438
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/CheckboxListModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.CheckboxList;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see CheckboxList
+ */
+public class CheckboxListModel extends TagModel {
+ public CheckboxListModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new CheckboxList(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/CheckboxModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/CheckboxModel.java
new file mode 100644
index 000000000..75f68d84e
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/CheckboxModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Checkbox;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Checkbox
+ */
+public class CheckboxModel extends TagModel {
+ public CheckboxModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Checkbox(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ComboBoxModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ComboBoxModel.java
new file mode 100644
index 000000000..48f620eb3
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ComboBoxModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ComboBox;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ComboBox
+ */
+public class ComboBoxModel extends TagModel {
+ public ComboBoxModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new ComboBox(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ComponentModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ComponentModel.java
new file mode 100644
index 000000000..8e8b851af
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ComponentModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.GenericUIBean;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ComponentModel
+ */
+public class ComponentModel extends TagModel {
+ public ComponentModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new GenericUIBean(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DateModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DateModel.java
new file mode 100644
index 000000000..56ac58386
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DateModel.java
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Date;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * DateModel
+ *
+ */
+public class DateModel extends TagModel {
+
+ public DateModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Date(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DatePickerModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DatePickerModel.java
new file mode 100644
index 000000000..51ee611e4
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DatePickerModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.DatePicker;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see DatePicker
+ */
+public class DatePickerModel extends TextFieldModel {
+ public DatePickerModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new DatePicker(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DivModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DivModel.java
new file mode 100644
index 000000000..2d4134536
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DivModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Div;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Div
+ */
+public class DivModel extends TagModel {
+ public DivModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Div(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DoubleSelectModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DoubleSelectModel.java
new file mode 100644
index 000000000..4baea67ab
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/DoubleSelectModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.DoubleSelect;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see DoubleSelect
+ */
+public class DoubleSelectModel extends TagModel {
+ public DoubleSelectModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new DoubleSelect(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ElseIfModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ElseIfModel.java
new file mode 100644
index 000000000..434324ed3
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ElseIfModel.java
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.ElseIf;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @version $Date$ $Id$
+ */
+public class ElseIfModel extends TagModel {
+
+ public ElseIfModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new ElseIf(stack);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ElseModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ElseModel.java
new file mode 100644
index 000000000..64ca5dd94
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ElseModel.java
@@ -0,0 +1,42 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Else;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ *
+ * @version $Date$ $Id$
+ */
+public class ElseModel extends TagModel {
+
+ public ElseModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Else(stack);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/FieldErrorModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/FieldErrorModel.java
new file mode 100644
index 000000000..93a5e3006
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/FieldErrorModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.FieldError;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see FieldError
+ */
+public class FieldErrorModel extends TagModel {
+ public FieldErrorModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new FieldError(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/FileModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/FileModel.java
new file mode 100644
index 000000000..495513671
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/FileModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.File;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see File
+ */
+public class FileModel extends TagModel {
+ public FileModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new File(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/FormModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/FormModel.java
new file mode 100644
index 000000000..a8b1b91bc
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/FormModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Form;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Form
+ */
+public class FormModel extends TagModel {
+ public FormModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Form(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/HeadModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/HeadModel.java
new file mode 100644
index 000000000..9100ed41b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/HeadModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Head;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Head
+ */
+public class HeadModel extends TagModel {
+ public HeadModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Head(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/HiddenModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/HiddenModel.java
new file mode 100644
index 000000000..5d2a87433
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/HiddenModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Hidden;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Hidden
+ */
+public class HiddenModel extends TagModel {
+ public HiddenModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Hidden(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/I18nModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/I18nModel.java
new file mode 100644
index 000000000..d2f90974c
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/I18nModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.I18n;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see I18n
+ */
+public class I18nModel extends TagModel {
+ public I18nModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new I18n(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/IfModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/IfModel.java
new file mode 100644
index 000000000..ef008c23f
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/IfModel.java
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.If;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @version $Date$ $Id$
+ */
+public class IfModel extends TagModel {
+
+
+ public IfModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new If(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/IncludeModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/IncludeModel.java
new file mode 100644
index 000000000..a74a67afb
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/IncludeModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Include;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Include
+ */
+public class IncludeModel extends TagModel {
+ public IncludeModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Include(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/IteratorModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/IteratorModel.java
new file mode 100644
index 000000000..f4ea11718
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/IteratorModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.IteratorComponent;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see IteratorComponent
+ */
+public class IteratorModel extends TagModel {
+ public IteratorModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new IteratorComponent(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/LabelModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/LabelModel.java
new file mode 100644
index 000000000..d8088e6f4
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/LabelModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Label;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Label
+ */
+public class LabelModel extends TagModel {
+ public LabelModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Label(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/OptGroupModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/OptGroupModel.java
new file mode 100644
index 000000000..8d193d734
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/OptGroupModel.java
@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.OptGroup;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * Freemarker's TransformModel for OptGroup component.
+ *
+ */
+public class OptGroupModel extends TagModel {
+ public OptGroupModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new OptGroup(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/OptionTransferSelectModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/OptionTransferSelectModel.java
new file mode 100644
index 000000000..15b76a7b1
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/OptionTransferSelectModel.java
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.OptionTransferSelect;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see OptionTransferSelect
+ */
+public class OptionTransferSelectModel extends TagModel {
+
+ public OptionTransferSelectModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new OptionTransferSelect(stack, req, res);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PanelModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PanelModel.java
new file mode 100644
index 000000000..00dc0858c
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PanelModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Panel;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Panel
+ */
+public class PanelModel extends TagModel {
+ public PanelModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Panel(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ParamModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ParamModel.java
new file mode 100644
index 000000000..74fb7792d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ParamModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Param;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Param
+ */
+public class ParamModel extends TagModel {
+ public ParamModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Param(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PasswordModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PasswordModel.java
new file mode 100644
index 000000000..6f9a4d87d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PasswordModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Password;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Password
+ */
+public class PasswordModel extends TagModel {
+ public PasswordModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Password(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PropertyModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PropertyModel.java
new file mode 100644
index 000000000..61e9fa034
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PropertyModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Property;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Property
+ */
+public class PropertyModel extends TagModel {
+ public PropertyModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Property(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PushModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PushModel.java
new file mode 100644
index 000000000..dc74f0d83
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/PushModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Push;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Push
+ */
+public class PushModel extends TagModel {
+ public PushModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Push(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/RadioModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/RadioModel.java
new file mode 100644
index 000000000..358be7f6d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/RadioModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Radio;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Radio
+ */
+public class RadioModel extends TagModel {
+ public RadioModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Radio(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ResetModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ResetModel.java
new file mode 100644
index 000000000..40efe93d6
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/ResetModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Reset;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see org.apache.struts2.components.Reset
+ */
+public class ResetModel extends TagModel {
+ public ResetModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Reset(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/SelectModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/SelectModel.java
new file mode 100644
index 000000000..43bc7460f
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/SelectModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Select;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Select
+ */
+public class SelectModel extends TagModel {
+ public SelectModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Select(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/SetModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/SetModel.java
new file mode 100644
index 000000000..f3828652b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/SetModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Set;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Set
+ */
+public class SetModel extends TagModel {
+ public SetModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Set(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/StrutsModels.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/StrutsModels.java
new file mode 100644
index 000000000..298a8388e
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/StrutsModels.java
@@ -0,0 +1,463 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * Provides @s.tag access for various tags.
+ *
+ */
+public class StrutsModels {
+ protected ValueStack stack;
+ protected HttpServletRequest req;
+ protected HttpServletResponse res;
+
+ protected ActionModel action;
+ protected BeanModel bean;
+ protected CheckboxModel checkbox;
+ protected CheckboxListModel checkboxlist;
+ protected ComboBoxModel comboBox;
+ protected ComponentModel component;
+ protected DateModel date;
+ protected DatePickerModel datepicker;
+ protected DivModel div;
+ protected DoubleSelectModel doubleselect;
+ protected FileModel file;
+ protected FormModel form;
+ protected HeadModel head;
+ protected HiddenModel hidden;
+ protected AnchorModel a;
+ protected I18nModel i18n;
+ protected IncludeModel include;
+ protected LabelModel label;
+ protected PanelModel panel;
+ protected PasswordModel password;
+ protected PushModel push;
+ protected ParamModel param;
+ protected RadioModel radio;
+ protected SelectModel select;
+ protected SetModel set;
+ protected SubmitModel submit;
+ protected ResetModel reset;
+ protected TabbedPanelModel tabbedPanel;
+ protected TextAreaModel textarea;
+ protected TextModel text;
+ protected TextFieldModel textfield;
+ protected TokenModel token;
+ protected URLModel url;
+ protected WebTableModel table;
+ protected PropertyModel property;
+ protected IteratorModel iterator;
+ protected ActionErrorModel actionerror;
+ protected ActionMessageModel actionmessage;
+ protected FieldErrorModel fielderror;
+ protected OptionTransferSelectModel optiontransferselect;
+ protected TreeModel treeModel;
+ protected UpDownSelectModel updownselect;
+ protected OptGroupModel optGroupModel;
+ protected IfModel ifModel;
+ protected ElseModel elseModel;
+ protected ElseIfModel elseIfModel;
+ protected TimePickerModel timePickerModel;
+
+
+ public StrutsModels(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ this.stack = stack;
+ this.req = req;
+ this.res = res;
+ }
+
+ public CheckboxListModel getCheckboxlist() {
+ if (checkboxlist == null) {
+ checkboxlist = new CheckboxListModel(stack, req, res);
+ }
+
+ return checkboxlist;
+ }
+
+ public CheckboxModel getCheckbox() {
+ if (checkbox == null) {
+ checkbox = new CheckboxModel(stack, req, res);
+ }
+
+ return checkbox;
+ }
+
+ public ComboBoxModel getComboBox() {
+ if (comboBox == null) {
+ comboBox = new ComboBoxModel(stack, req, res);
+ }
+
+ return comboBox;
+ }
+
+ public ComponentModel getComponent() {
+ if (component == null) {
+ component = new ComponentModel(stack, req, res);
+ }
+
+ return component;
+ }
+
+ public DoubleSelectModel getDoubleselect() {
+ if (doubleselect == null) {
+ doubleselect = new DoubleSelectModel(stack, req, res);
+ }
+
+ return doubleselect;
+ }
+
+ public FileModel getFile() {
+ if (file == null) {
+ file = new FileModel(stack, req, res);
+ }
+
+ return file;
+ }
+
+ public FormModel getForm() {
+ if (form == null) {
+ form = new FormModel(stack, req, res);
+ }
+
+ return form;
+ }
+
+ public HeadModel getHead() {
+ if (head == null) {
+ head = new HeadModel(stack, req, res);
+ }
+
+ return head;
+ }
+
+ public HiddenModel getHidden() {
+ if (hidden == null) {
+ hidden = new HiddenModel(stack, req, res);
+ }
+
+ return hidden;
+ }
+ public LabelModel getLabel() {
+ if (label == null) {
+ label = new LabelModel(stack, req, res);
+ }
+
+ return label;
+ }
+
+ public PasswordModel getPassword() {
+ if (password == null) {
+ password = new PasswordModel(stack, req, res);
+ }
+
+ return password;
+ }
+
+ public RadioModel getRadio() {
+ if (radio == null) {
+ radio = new RadioModel(stack, req, res);
+ }
+
+ return radio;
+ }
+
+ public SelectModel getSelect() {
+ if (select == null) {
+ select = new SelectModel(stack, req, res);
+ }
+
+ return select;
+ }
+
+ public SubmitModel getSubmit() {
+ if (submit == null) {
+ submit = new SubmitModel(stack, req, res);
+ }
+
+ return submit;
+ }
+
+ public ResetModel getReset() {
+ if (reset == null) {
+ reset = new ResetModel(stack, req, res);
+ }
+
+ return reset;
+ }
+
+ public TextAreaModel getTextarea() {
+ if (textarea == null) {
+ textarea = new TextAreaModel(stack, req, res);
+ }
+
+ return textarea;
+ }
+
+ public TextFieldModel getTextfield() {
+ if (textfield == null) {
+ textfield = new TextFieldModel(stack, req, res);
+ }
+
+ return textfield;
+ }
+
+ public DateModel getDate() {
+ if (date == null) {
+ date = new DateModel(stack, req, res);
+ }
+
+ return date;
+ }
+
+ public DatePickerModel getDatepicker() {
+ if (datepicker == null) {
+ datepicker = new DatePickerModel(stack, req, res);
+ }
+
+ return datepicker;
+ }
+
+ public TokenModel getToken() {
+ if (token == null) {
+ token = new TokenModel(stack, req, res);
+ }
+
+ return token;
+ }
+
+ public WebTableModel getTable() {
+ if (table == null) {
+ table = new WebTableModel(stack, req, res);
+ }
+
+ return table;
+ }
+
+ public URLModel getUrl() {
+ if (url == null) {
+ url = new URLModel(stack, req, res);
+ }
+
+ return url;
+ }
+
+ public IncludeModel getInclude() {
+ if (include == null) {
+ include = new IncludeModel(stack, req, res);
+ }
+
+ return include;
+ }
+
+ public ParamModel getParam() {
+ if (param == null) {
+ param = new ParamModel(stack, req, res);
+ }
+
+ return param;
+ }
+
+ public ActionModel getAction() {
+ if (action == null) {
+ action = new ActionModel(stack, req, res);
+ }
+
+ return action;
+ }
+
+ public AnchorModel getA() {
+ if (a == null) {
+ a = new AnchorModel(stack, req, res);
+ }
+
+ return a;
+ }
+
+ public AnchorModel getHref() {
+ if (a == null) {
+ a = new AnchorModel(stack, req, res);
+ }
+
+ return a;
+ }
+
+ public DivModel getDiv() {
+ if (div == null) {
+ div = new DivModel(stack, req, res);
+ }
+
+ return div;
+ }
+
+ public TextModel getText() {
+ if (text == null) {
+ text = new TextModel(stack, req, res);
+ }
+
+ return text;
+ }
+
+ public TabbedPanelModel getTabbedPanel() {
+ if (tabbedPanel == null) {
+ tabbedPanel = new TabbedPanelModel(stack, req, res);
+ }
+
+ return tabbedPanel;
+ }
+
+ public PanelModel getPanel() {
+ if (panel == null) {
+ panel = new PanelModel(stack, req, res);
+ }
+
+ return panel;
+ }
+
+ public BeanModel getBean() {
+ if (bean == null) {
+ bean = new BeanModel(stack, req, res);
+ }
+
+ return bean;
+ }
+
+ public I18nModel getI18n() {
+ if (i18n == null) {
+ i18n = new I18nModel(stack, req, res);
+ }
+
+ return i18n;
+ }
+
+ public PushModel getPush() {
+ if (push == null) {
+ push = new PushModel(stack, req, res);
+ }
+
+ return push;
+ }
+
+ public SetModel getSet() {
+ if (set == null) {
+ set = new SetModel(stack, req, res);
+ }
+
+ return set;
+ }
+
+ public PropertyModel getProperty() {
+ if (property == null) {
+ property = new PropertyModel(stack, req, res);
+ }
+
+ return property;
+ }
+
+ public IteratorModel getIterator() {
+ if (iterator == null) {
+ iterator = new IteratorModel(stack, req, res);
+ }
+
+ return iterator;
+ }
+
+ public ActionErrorModel getActionerror() {
+ if (actionerror == null) {
+ actionerror = new ActionErrorModel(stack, req, res);
+ }
+
+ return actionerror;
+ }
+
+ public ActionMessageModel getActionmessage() {
+ if (actionmessage == null) {
+ actionmessage = new ActionMessageModel(stack, req, res);
+ }
+
+ return actionmessage;
+ }
+
+ public FieldErrorModel getFielderror() {
+ if (fielderror == null) {
+ fielderror = new FieldErrorModel(stack, req, res);
+ }
+
+ return fielderror;
+ }
+
+ public OptionTransferSelectModel getOptiontransferselect() {
+ if (optiontransferselect == null) {
+ optiontransferselect = new OptionTransferSelectModel(stack, req, res);
+ }
+ return optiontransferselect;
+ }
+
+ public TreeModel getTree() {
+ if (treeModel == null) {
+ treeModel = new TreeModel(stack,req, res);
+ }
+ return treeModel;
+ }
+
+ public UpDownSelectModel getUpdownselect() {
+ if (updownselect == null) {
+ updownselect = new UpDownSelectModel(stack, req, res);
+ }
+ return updownselect;
+ }
+
+ public OptGroupModel getOptgroup() {
+ if (optGroupModel == null) {
+ optGroupModel = new OptGroupModel(stack, req, res);
+ }
+ return optGroupModel;
+ }
+
+ public IfModel getIf() {
+ if (ifModel == null) {
+ ifModel = new IfModel(stack, req, res);
+ }
+ return ifModel;
+ }
+
+ public ElseModel getElse() {
+ if (elseModel == null) {
+ elseModel = new ElseModel(stack, req, res);
+ }
+ return elseModel;
+ }
+
+ public ElseIfModel getElseif() {
+ if (elseIfModel == null) {
+ elseIfModel = new ElseIfModel(stack, req, res);
+ }
+ return elseIfModel;
+ }
+
+ public TimePickerModel getTimepicker() {
+ if (timePickerModel == null) {
+ timePickerModel = new TimePickerModel(stack, req, res);
+ }
+ return timePickerModel;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/SubmitModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/SubmitModel.java
new file mode 100644
index 000000000..6d91e4fa6
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/SubmitModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Submit;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Submit
+ */
+public class SubmitModel extends TagModel {
+ public SubmitModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Submit(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TabbedPanelModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TabbedPanelModel.java
new file mode 100644
index 000000000..00db842a9
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TabbedPanelModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TabbedPanel;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see TabbedPanel
+ */
+public class TabbedPanelModel extends TagModel {
+ public TabbedPanelModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new TabbedPanel(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java
new file mode 100644
index 000000000..4b89a654c
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java
@@ -0,0 +1,102 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+import freemarker.template.SimpleNumber;
+import freemarker.template.SimpleSequence;
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateTransformModel;
+
+public abstract class TagModel implements TemplateTransformModel {
+ private static final Log LOG = LogFactory.getLog(TagModel.class);
+
+ protected ValueStack stack;
+ protected HttpServletRequest req;
+ protected HttpServletResponse res;
+
+ public TagModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ this.stack = stack;
+ this.req = req;
+ this.res = res;
+ }
+
+ public Writer getWriter(Writer writer, Map params) throws TemplateModelException, IOException {
+ Component bean = getBean();
+ Map basicParams = convertParams(params);
+ bean.copyParams(basicParams);
+ bean.addAllParameters(getComplexParams(params));
+ return new CallbackWriter(bean, writer);
+ }
+
+ protected abstract Component getBean();
+
+ private Map convertParams(Map params) {
+ HashMap map = new HashMap(params.size());
+ for (Iterator iterator = params.entrySet().iterator(); iterator.hasNext();) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ Object value = entry.getValue();
+ if (value != null && !complexType(value)) {
+ map.put(entry.getKey(), value.toString());
+ }
+ }
+ return map;
+ }
+
+ private Map getComplexParams(Map params) {
+ HashMap map = new HashMap(params.size());
+ for (Iterator iterator = params.entrySet().iterator(); iterator.hasNext();) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ Object value = entry.getValue();
+ if (value != null && complexType(value)) {
+ if (value instanceof freemarker.ext.beans.BeanModel) {
+ map.put(entry.getKey(), ((freemarker.ext.beans.BeanModel) value).getWrappedObject());
+ } else if (value instanceof SimpleNumber) {
+ map.put(entry.getKey(), ((SimpleNumber) value).getAsNumber());
+ } else if (value instanceof SimpleSequence) {
+ try {
+ map.put(entry.getKey(), ((SimpleSequence) value).toList());
+ } catch (TemplateModelException e) {
+ LOG.error("There was a problem converting a SimpleSequence to a list", e);
+ }
+ }
+ }
+ }
+ return map;
+ }
+
+ private boolean complexType(Object value) {
+ return value instanceof freemarker.ext.beans.BeanModel
+ || value instanceof SimpleNumber
+ || value instanceof SimpleSequence;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TextAreaModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TextAreaModel.java
new file mode 100644
index 000000000..69f7ada39
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TextAreaModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TextArea;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see TextArea
+ */
+public class TextAreaModel extends TagModel {
+ public TextAreaModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new TextArea(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TextFieldModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TextFieldModel.java
new file mode 100644
index 000000000..d80d5c693
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TextFieldModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TextField;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see TextField
+ */
+public class TextFieldModel extends TagModel {
+ public TextFieldModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new TextField(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TextModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TextModel.java
new file mode 100644
index 000000000..e14f05129
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TextModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Text;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Text
+ */
+public class TextModel extends TagModel {
+ public TextModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Text(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TimePickerModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TimePickerModel.java
new file mode 100644
index 000000000..fef0b76e9
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TimePickerModel.java
@@ -0,0 +1,21 @@
+package org.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TimePicker;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+public class TimePickerModel extends TagModel {
+
+ public TimePickerModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new TimePicker(stack, req, res);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TokenModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TokenModel.java
new file mode 100644
index 000000000..0d800a990
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TokenModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Token;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Token
+ */
+public class TokenModel extends TagModel {
+ public TokenModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Token(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TreeModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TreeModel.java
new file mode 100644
index 000000000..b27a238fc
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TreeModel.java
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Tree;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * TreeModel
+ * @see Tree
+ *
+ */
+public class TreeModel extends TagModel {
+ public TreeModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new Tree(stack,req,res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TreeNodeModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TreeNodeModel.java
new file mode 100644
index 000000000..c2735e3bc
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/TreeNodeModel.java
@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TreeNode;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * TreeNodeModel
+ * @see TreeNode
+ */
+public class TreeNodeModel extends TagModel {
+ public TreeNodeModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new TreeNode(stack,req,res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/URLModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/URLModel.java
new file mode 100644
index 000000000..0b3cd8557
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/URLModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.URL;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see URL
+ */
+public class URLModel extends TagModel {
+ public URLModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new URL(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/UpDownSelectModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/UpDownSelectModel.java
new file mode 100644
index 000000000..484cacbbe
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/UpDownSelectModel.java
@@ -0,0 +1,42 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.UpDownSelect;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see UpDownSelect
+ *
+ */
+public class UpDownSelectModel extends TagModel {
+
+ public UpDownSelectModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new UpDownSelect(stack, req, res);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/WebTableModel.java b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/WebTableModel.java
new file mode 100644
index 000000000..2757dbfba
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/freemarker/tags/WebTableModel.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.freemarker.tags;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.table.WebTable;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see WebTable
+ */
+public class WebTableModel extends TagModel {
+ public WebTableModel(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ super(stack, req, res);
+ }
+
+ protected Component getBean() {
+ return new WebTable(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ActionTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ActionTag.java
new file mode 100644
index 000000000..59fe8dcd5
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ActionTag.java
@@ -0,0 +1,75 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ActionComponent;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ActionComponent
+ */
+public class ActionTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -5384167073331678855L;
+
+ protected String name;
+ protected String namespace;
+ protected boolean executeResult;
+ protected boolean ignoreContextParams;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new ActionComponent(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ActionComponent action = (ActionComponent) component;
+ action.setName(name);
+ action.setNamespace(namespace);
+ action.setExecuteResult(executeResult);
+ action.setIgnoreContextParams(ignoreContextParams);
+ action.start(pageContext.getOut());
+ }
+
+ protected void addParameter(String name, Object value) {
+ ActionComponent ac = (ActionComponent) component;
+ ac.addParameter(name, value);
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public void setExecuteResult(boolean executeResult) {
+ this.executeResult = executeResult;
+ }
+
+ public void setIgnoreContextParams(boolean ignoreContextParams) {
+ this.ignoreContextParams = ignoreContextParams;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/BeanTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/BeanTag.java
new file mode 100644
index 000000000..1316ace2f
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/BeanTag.java
@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.components.Bean;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Bean
+ */
+public class BeanTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -3863152522071209267L;
+
+ protected static Log log = LogFactory.getLog(BeanTag.class);
+
+ protected String name;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Bean(stack);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ((Bean) component).setName(name);
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ComponentTagSupport.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ComponentTagSupport.java
new file mode 100644
index 000000000..32ba71620
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ComponentTagSupport.java
@@ -0,0 +1,60 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspException;
+
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ */
+public abstract class ComponentTagSupport extends StrutsBodyTagSupport {
+ protected Component component;
+
+ public abstract Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res);
+
+ public int doEndTag() throws JspException {
+ component.end(pageContext.getOut(), getBody());
+ component = null;
+ return EVAL_PAGE;
+ }
+
+ public int doStartTag() throws JspException {
+ component = getBean(getStack(), (HttpServletRequest) pageContext.getRequest(), (HttpServletResponse) pageContext.getResponse());
+ populateParams();
+ boolean evalBody = component.start(pageContext.getOut());
+
+ if (evalBody) {
+ return component.usesBody() ? EVAL_BODY_BUFFERED : EVAL_BODY_INCLUDE;
+ } else {
+ return SKIP_BODY;
+ }
+ }
+
+ protected void populateParams() {
+ component.setId(id);
+ }
+
+ public Component getComponent() {
+ return component;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/DateTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/DateTag.java
new file mode 100644
index 000000000..893cb83db
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/DateTag.java
@@ -0,0 +1,63 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Date;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Date
+ */
+public class DateTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -6216963123295613440L;
+
+ protected String name;
+ protected String format;
+ protected boolean nice;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Date(stack);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+ Date d = (Date)component;
+ d.setName(name);
+ d.setFormat(format);
+ d.setNice(nice);
+
+ }
+
+ public void setFormat(String format) {
+ this.format = format;
+ }
+
+ public void setNice(boolean nice) {
+ this.nice = nice;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ElseIfTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ElseIfTag.java
new file mode 100644
index 000000000..d52c853ce
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ElseIfTag.java
@@ -0,0 +1,48 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.ElseIf;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ElseIf
+ */
+public class ElseIfTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -3872016920741400345L;
+
+ protected String test;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new ElseIf(stack);
+ }
+
+ protected void populateParams() {
+ ((ElseIf) getComponent()).setTest(test);
+ }
+
+ public void setTest(String test) {
+ this.test = test;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ElseTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ElseTag.java
new file mode 100644
index 000000000..7954e26de
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ElseTag.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Else;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Else
+ */
+public class ElseTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = 8166807953193406785L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Else(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/I18nTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/I18nTag.java
new file mode 100644
index 000000000..49b24a9e2
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/I18nTag.java
@@ -0,0 +1,51 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.I18n;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see I18n
+ */
+public class I18nTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -7914587341936116887L;
+
+ protected String name;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new I18n(stack);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ((I18n) component).setName(name);
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/IfTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/IfTag.java
new file mode 100644
index 000000000..6f5bdcda0
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/IfTag.java
@@ -0,0 +1,49 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.If;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see If
+ */
+public class IfTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = 4448870162549923833L;
+
+ String test;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new If(stack);
+ }
+
+ protected void populateParams() {
+ ((If) getComponent()).setTest(test);
+ }
+
+ public void setTest(String test) {
+ this.test = test;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/IncludeTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/IncludeTag.java
new file mode 100644
index 000000000..a2d45bf3a
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/IncludeTag.java
@@ -0,0 +1,51 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Include;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Include
+ */
+public class IncludeTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -1585165567043278243L;
+
+ protected String value;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Include(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ((Include) component).setValue(value);
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/IteratorStatus.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/IteratorStatus.java
new file mode 100644
index 000000000..ff216b0b3
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/IteratorStatus.java
@@ -0,0 +1,74 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+
+/**
+ * The iterator tag can export an IteratorStatus object so that
+ * one can get information about the status of the iteration, such as
+ * the size, current index, and whether any more items are available.
+ *
+ */
+public class IteratorStatus {
+ protected StatusState state;
+
+ public IteratorStatus(StatusState aState) {
+ state = aState;
+ }
+
+ public int getCount() {
+ return state.index + 1;
+ }
+
+ public boolean isEven() {
+ return ((state.index + 1) % 2) == 0;
+ }
+
+ public boolean isFirst() {
+ return state.index == 0;
+ }
+
+ public int getIndex() {
+ return state.index;
+ }
+
+ public boolean isLast() {
+ return state.last;
+ }
+
+ public boolean isOdd() {
+ return ((state.index + 1) % 2) == 1;
+ }
+
+ public int modulus(int operand) {
+ return (state.index + 1) % operand;
+ }
+
+ public static class StatusState {
+ boolean last = false;
+ int index = 0;
+
+ public void setLast(boolean isLast) {
+ last = isLast;
+ }
+
+ public void next() {
+ index++;
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/IteratorTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/IteratorTag.java
new file mode 100644
index 000000000..1c944ff60
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/IteratorTag.java
@@ -0,0 +1,81 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspException;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.IteratorComponent;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see IteratorComponent
+ */
+public class IteratorTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -1827978135193581901L;
+
+ protected String statusAttr;
+ protected String value;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new IteratorComponent(stack);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ IteratorComponent tag = (IteratorComponent) getComponent();
+ tag.setStatus(statusAttr);
+ tag.setValue(value);
+ }
+
+ public void setStatus(String status) {
+ this.statusAttr = status;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public int doEndTag() throws JspException {
+ component = null;
+ return EVAL_PAGE;
+ }
+
+ public int doAfterBody() throws JspException {
+ boolean again = component.end(pageContext.getOut(), getBody());
+
+ if (again) {
+ return EVAL_BODY_AGAIN;
+ } else {
+ if (bodyContent != null) {
+ try {
+ bodyContent.writeOut(bodyContent.getEnclosingWriter());
+ } catch (Exception e) {
+ throw new JspException(e.getMessage());
+ }
+ }
+ return SKIP_BODY;
+ }
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ParamTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ParamTag.java
new file mode 100644
index 000000000..fd67c5d2f
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ParamTag.java
@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Param;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Param
+ */
+public class ParamTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -968332732207156408L;
+
+ protected String name;
+ protected String value;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Param(stack);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ Param param = (Param) component;
+ param.setName(name);
+ param.setValue(value);
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/PropertyTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/PropertyTag.java
new file mode 100644
index 000000000..ffd925aee
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/PropertyTag.java
@@ -0,0 +1,64 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Property;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Property
+ */
+public class PropertyTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = 435308349113743852L;
+
+ private String defaultValue;
+ private String value;
+ private boolean escape = true;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Property(stack);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ Property tag = (Property) component;
+ tag.setDefault(defaultValue);
+ tag.setValue(value);
+ tag.setEscape(escape);
+ }
+
+ public void setDefault(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ public void setEscape(boolean escape) {
+ this.escape = escape;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/PushTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/PushTag.java
new file mode 100644
index 000000000..6645f4a7b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/PushTag.java
@@ -0,0 +1,51 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Push;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Push
+ */
+public class PushTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -1357895305148907931L;
+
+ protected String value;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Push(stack);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ((Push) component).setValue(value);
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/SetTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/SetTag.java
new file mode 100644
index 000000000..13170ed48
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/SetTag.java
@@ -0,0 +1,64 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Set;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Set
+ */
+public class SetTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -5074213926790716974L;
+
+ protected String name;
+ protected String scope;
+ protected String value;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Set(stack);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ Set set = (Set) component;
+ set.setName(name);
+ set.setScope(scope);
+ set.setValue(value);
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/StrutsBodyTagSupport.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/StrutsBodyTagSupport.java
new file mode 100644
index 000000000..8b7b7e5ce
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/StrutsBodyTagSupport.java
@@ -0,0 +1,124 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import java.io.PrintWriter;
+
+import javax.servlet.jsp.tagext.BodyTagSupport;
+
+import org.apache.struts2.util.FastByteArrayOutputStream;
+import org.apache.struts2.views.util.ContextUtil;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * Contains common functonalities for Struts JSP Tags.
+ *
+ */
+public class StrutsBodyTagSupport extends BodyTagSupport {
+
+ private static final long serialVersionUID = -1201668454354226175L;
+
+ /**
+ * @s.tagattribute required="false" type="String"
+ * description="The id of the tag element."
+ */
+ public void setId(String string) {
+ super.setId(string);
+ }
+
+ protected boolean altSyntax() {
+ return ContextUtil.isUseAltSyntax(getStack().getContext());
+ }
+
+ protected ValueStack getStack() {
+ return TagUtils.getStack(pageContext);
+ }
+
+ protected String findString(String expr) {
+ return (String) findValue(expr, String.class);
+ }
+
+ protected Object findValue(String expr) {
+ if (altSyntax()) {
+ // does the expression start with %{ and end with }? if so, just cut it off!
+ if (expr.startsWith("%{") && expr.endsWith("}")) {
+ expr = expr.substring(2, expr.length() - 1);
+ }
+ }
+
+ return getStack().findValue(expr);
+ }
+
+ protected Object findValue(String expr, Class toType) {
+ if (altSyntax() && toType == String.class) {
+ return translateVariables(expr, getStack());
+ } else {
+ if (altSyntax()) {
+ // does the expression start with %{ and end with }? if so, just cut it off!
+ if (expr.startsWith("%{") && expr.endsWith("}")) {
+ expr = expr.substring(2, expr.length() - 1);
+ }
+ }
+
+ return getStack().findValue(expr, toType);
+ }
+ }
+
+ protected String toString(Throwable t) {
+ FastByteArrayOutputStream bout = new FastByteArrayOutputStream();
+ PrintWriter wrt = new PrintWriter(bout);
+ t.printStackTrace(wrt);
+ wrt.close();
+
+ return bout.toString();
+ }
+
+ protected String getBody() {
+ if (bodyContent == null) {
+ return "";
+ } else {
+ return bodyContent.getString().trim();
+ }
+ }
+
+ public static String translateVariables(String expression, ValueStack stack) {
+ while (true) {
+ int x = expression.indexOf("%{");
+ int y = expression.indexOf("}", x);
+
+ if ((x != -1) && (y != -1)) {
+ String var = expression.substring(x + 2, y);
+
+ Object o = stack.findValue(var, String.class);
+
+ if (o != null) {
+ expression = expression.substring(0, x) + o + expression.substring(y + 1);
+ } else {
+ // the variable doesn't exist, so don't display anything
+ expression = expression.substring(0, x) + expression.substring(y + 1);
+ }
+ } else {
+ break;
+ }
+ }
+
+ return expression;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/TagUtils.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/TagUtils.java
new file mode 100644
index 000000000..cb7c1d778
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/TagUtils.java
@@ -0,0 +1,104 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.struts2.RequestUtils;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.dispatcher.ApplicationMap;
+import org.apache.struts2.dispatcher.Dispatcher;
+import org.apache.struts2.dispatcher.RequestMap;
+import org.apache.struts2.dispatcher.SessionMap;
+import org.apache.struts2.dispatcher.mapper.ActionMapper;
+import org.apache.struts2.dispatcher.mapper.ActionMapperFactory;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.apache.struts2.util.AttributeMap;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ValueStackFactory;
+
+
+/**
+ */
+public class TagUtils {
+
+ public static ValueStack getStack(PageContext pageContext) {
+ HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
+ ValueStack stack = (ValueStack) req.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
+
+ if (stack == null) {
+ stack = ValueStackFactory.getFactory().createValueStack();
+
+ HttpServletResponse res = (HttpServletResponse) pageContext.getResponse();
+ Dispatcher du = Dispatcher.getInstance();
+ Map extraContext = du.createContextMap(new RequestMap(req),
+ req.getParameterMap(),
+ new SessionMap(req),
+ new ApplicationMap(pageContext.getServletContext()),
+ req,
+ res,
+ pageContext.getServletContext());
+ extraContext.put(ServletActionContext.PAGE_CONTEXT, pageContext);
+ stack.getContext().putAll(extraContext);
+ req.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
+
+ // also tie this stack/context to the ThreadLocal
+ ActionContext.setContext(new ActionContext(stack.getContext()));
+ } else {
+ // let's make sure that the current page context is in the action context
+ Map context = stack.getContext();
+ context.put(ServletActionContext.PAGE_CONTEXT, pageContext);
+
+ AttributeMap attrMap = new AttributeMap(context);
+ context.put("attr", attrMap);
+ }
+
+ return stack;
+ }
+
+ public static String buildNamespace(ValueStack stack, HttpServletRequest request) {
+ ActionContext context = new ActionContext(stack.getContext());
+ ActionInvocation invocation = context.getActionInvocation();
+
+ if (invocation == null) {
+ ActionMapper mapper = ActionMapperFactory.getMapper();
+ ActionMapping mapping = mapper.getMapping(request,
+ Dispatcher.getInstance().getConfigurationManager());
+
+ if (mapping != null) {
+ return mapping.getNamespace();
+ } else {
+ // well, if the ActionMapper can't tell us, and there is no existing action invocation,
+ // let's just go with a default guess that the namespace is the last the path minus the
+ // last part (/foo/bar/baz.xyz -> /foo/bar)
+
+ String path = RequestUtils.getServletPath(request);
+ return path.substring(0, path.lastIndexOf("/"));
+ }
+ } else {
+ return invocation.getProxy().getNamespace();
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/TextTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/TextTag.java
new file mode 100644
index 000000000..ebcdc549a
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/TextTag.java
@@ -0,0 +1,51 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Text;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Text
+ */
+public class TextTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -3075088084198264581L;
+
+ protected String name;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Text(stack);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ((Text) component).setName(name);
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/URLTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/URLTag.java
new file mode 100644
index 000000000..9fbf7780d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/URLTag.java
@@ -0,0 +1,120 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.URL;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see URL
+ */
+public class URLTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = 1722460444125206226L;
+
+ protected String includeParams;
+ protected String scheme;
+ protected String value;
+ protected String action;
+ protected String namespace;
+ protected String method;
+ protected String encode;
+ protected String includeContext;
+ protected String portletMode;
+ protected String windowState;
+ protected String portletUrlType;
+ protected String anchor;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new URL(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ URL url = (URL) component;
+ url.setIncludeParams(includeParams);
+ url.setScheme(scheme);
+ url.setValue(value);
+ url.setMethod(method);
+ url.setNamespace(namespace);
+ url.setAction(action);
+ url.setPortletMode(portletMode);
+ url.setPortletUrlType(portletUrlType);
+ url.setWindowState(windowState);
+ url.setAnchor(anchor);
+
+ if (encode != null) {
+ url.setEncode(Boolean.valueOf(encode).booleanValue());
+ }
+ if (includeContext != null) {
+ url.setIncludeContext(Boolean.valueOf(includeContext).booleanValue());
+ }
+ }
+
+ public void setEncode(String encode) {
+ this.encode = encode;
+ }
+
+ public void setIncludeContext(String includeContext) {
+ this.includeContext = includeContext;
+ }
+
+ public void setIncludeParams(String name) {
+ includeParams = name;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public void setScheme(String scheme) {
+ this.scheme = scheme;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+ public void setPortletMode(String portletMode) {
+ this.portletMode = portletMode;
+ }
+ public void setPortletUrlType(String portletUrlType) {
+ this.portletUrlType = portletUrlType;
+ }
+ public void setWindowState(String windowState) {
+ this.windowState = windowState;
+ }
+
+ public void setAnchor(String anchor) {
+ this.anchor = anchor;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/AppendIteratorTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/AppendIteratorTag.java
new file mode 100644
index 000000000..0d68b356b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/AppendIteratorTag.java
@@ -0,0 +1,44 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.iterator;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.AppendIterator;
+import org.apache.struts2.components.Component;
+import org.apache.struts2.views.jsp.ComponentTagSupport;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * Append a list of iterators. The values of the iterators will be merged
+ * into one iterator.
+ *
+ * @see AppendIterator
+ */
+public class AppendIteratorTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = -6017337859763283691L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new AppendIterator(stack);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/IteratorGeneratorTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/IteratorGeneratorTag.java
new file mode 100644
index 000000000..90e4c921b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/IteratorGeneratorTag.java
@@ -0,0 +1,253 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.iterator;
+
+import javax.servlet.jsp.JspException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.util.IteratorGenerator;
+import org.apache.struts2.util.IteratorGenerator.Converter;
+import org.apache.struts2.views.jsp.StrutsBodyTagSupport;
+
+
+/**
+ *
+ * NOTE: JSP-TAG
+ *
+ * Generate an iterator based on the val attribute supplied.
+ *
+ * NOTE: The generated iterator will ALWAYS be pushed into the top of the stack, and poped
+ * at the end of the tag.
+ *
+ *
+ *
+ *
+ * val* (Object) - the source to be parsed into an iterator
+ * count (Object) - the max number (Integer, Float, Double, Long, String) entries to be in the iterator
+ * separator (String) - the separator to be used in separating the val into entries of the iterator
+ * id (String) - the id to store the resultant iterator into page context, if such id is supplied
+ * converter (Object) - the converter (must extends off IteratorGenerator.Converter interface) to convert the String entry parsed from val into an object
+ *
+ *
+ *
+ *
+ *
+ * Example One:
+ *
+ * Generate a simple iterator
+ * <s:generator val="%{'aaa,bbb,ccc,ddd,eee'}">
+ * <s:iterator>
+ * <s:property /><br/>
+ * </s:iterator>
+ * </s:generator>
+ *
+ * This generates an iterator and print it out using the iterator tag.
+ *
+ * Example Two:
+ *
+ * Generate an iterator with count attribute
+ * <s:generator val="%{'aaa,bbb,ccc,ddd,eee'}" count="3">
+ * <s:iterator>
+ * <s:property /><br/>
+ * </s:iterator>
+ * </s:generator>
+ *
+ * This generates an iterator, but only 3 entries will be available in the iterator
+ * generated, namely aaa, bbb and ccc respectively because count attribute is set to 3
+ *
+ * Example Three:
+ *
+ * Generate an iterator with id attribute
+ * <s:generator val="%{'aaa,bbb,ccc,ddd,eee'}" count="4" separator="," id="myAtt" />
+ * <%
+ * Iterator i = (Iterator) pageContext.getAttribute("myAtt");
+ * while(i.hasNext()) {
+ * String s = (String) i.next(); %>
+ * <%=s%> <br/>
+ * <% }
+ * %>
+ *
+ * This generates an iterator and put it in the PageContext under the key as specified
+ * by the id attribute.
+ *
+ *
+ * Example Four:
+ *
+ * Generate an iterator with comparator attribute
+ * <s:generator val="%{'aaa,bbb,ccc,ddd,eee'}" converter="%{myConverter}">
+ * <s:iterator>
+ * <s:property /><br/>
+ * </s:iterator>
+ * </s:generator>
+ *
+ *
+ * public class GeneratorTagAction extends ActionSupport {
+ *
+ * ....
+ *
+ * public Converter getMyConverter() {
+ * return new Converter() {
+ * public Object convert(String value) throws Exception {
+ * return "converter-"+value;
+ * }
+ * };
+ * }
+ *
+ * ...
+ *
+ * }
+ *
+ * This will generate an iterator with each entries decided by the converter supplied. With
+ * this converter, it simply add "converter-" to each entries.
+ *
+ *
+ * @see org.apache.struts2.util.IteratorGenerator
+ *
+ * @s.tag name="generator" tld-body-content="JSP"
+ * description="Generate an iterator for a iterable source."
+ */
+public class IteratorGeneratorTag extends StrutsBodyTagSupport {
+
+ private static final long serialVersionUID = 2968037295463973936L;
+
+ public static final String DEFAULT_SEPARATOR = ",";
+
+ private static final Log _log = LogFactory.getLog(IteratorGeneratorTag.class);
+
+ String countAttr;
+ String separatorAttr;
+ String valueAttr;
+ String converterAttr;
+
+ IteratorGenerator iteratorGenerator = null;
+
+ /**
+ * @s.tagattribute required="false" type="Integer"
+ * description="the max number entries to be in the iterator"
+ */
+ public void setCount(String count) {
+ countAttr = count;
+ }
+
+ /**
+ * @s.tagattribute required="true" type="String"
+ * description="the separator to be used in separating the val into entries of the iterator"
+ */
+ public void setSeparator(String separator) {
+ separatorAttr = separator;
+ }
+
+ /**
+ * @s.tagattribute required="true"
+ * description="the source to be parsed into an iterator"
+ */
+ public void setVal(String val) {
+ valueAttr = val;
+ }
+
+ /**
+ * @s.tagattribute required="false" type="org.apache.struts2.util.IteratorGenerator.Converter"
+ * description="the converter to convert the String entry parsed from val into an object"
+ */
+ public void setConverter(String aConverter) {
+ converterAttr = aConverter;
+ }
+
+ /**
+ * @s.tagattribute required="false" type="String"
+ * description="the id to store the resultant iterator into page context, if such id is supplied"
+ */
+ public void setId(String string) {
+ super.setId(string);
+ }
+
+ public int doStartTag() throws JspException {
+
+ // value
+ Object value = findValue(valueAttr);
+
+ // separator
+ String separator = DEFAULT_SEPARATOR;
+ if (separatorAttr != null && separatorAttr.length() > 0) {
+ separator = findString(separatorAttr);
+ }
+
+ // TODO: maybe this could be put into an Util class, or there is already one?
+ // count
+ int count = 0;
+ if (countAttr != null && countAttr.length() > 0) {
+ Object countObj = findValue(countAttr);
+ if (countObj instanceof Integer) {
+ count = ((Integer)countObj).intValue();
+ }
+ else if (countObj instanceof Float) {
+ count = ((Float)countObj).intValue();
+ }
+ else if (countObj instanceof Long) {
+ count = ((Long)countObj).intValue();
+ }
+ else if (countObj instanceof Double) {
+ count = ((Long)countObj).intValue();
+ }
+ else if (countObj instanceof String) {
+ try {
+ count = Integer.parseInt((String)countObj);
+ }
+ catch(NumberFormatException e) {
+ _log.warn("unable to convert count attribute ["+countObj+"] to number, ignore count attribute", e);
+ }
+ }
+ }
+
+ // converter
+ Converter converter = null;
+ if (converterAttr != null && converterAttr.length() > 0) {
+ converter = (Converter) findValue(converterAttr);
+ }
+
+
+ iteratorGenerator = new IteratorGenerator();
+ iteratorGenerator.setValues(value);
+ iteratorGenerator.setCount(count);
+ iteratorGenerator.setSeparator(separator);
+ iteratorGenerator.setConverter(converter);
+
+ iteratorGenerator.execute();
+
+
+
+ // push resulting iterator into stack
+ getStack().push(iteratorGenerator);
+ if (getId() != null && getId().length() > 0) {
+ // if an id is specified, we have the resulting iterator set into
+ // the pageContext attribute as well
+ pageContext.setAttribute(getId(), iteratorGenerator);
+ }
+
+ return EVAL_BODY_INCLUDE;
+ }
+
+ public int doEndTag() throws JspException {
+ // pop resulting iterator from stack at end tag
+ getStack().pop();
+ iteratorGenerator = null; // clean up
+
+ return EVAL_PAGE;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/MergeIteratorTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/MergeIteratorTag.java
new file mode 100644
index 000000000..9d3eec513
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/MergeIteratorTag.java
@@ -0,0 +1,45 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.iterator;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.MergeIterator;
+import org.apache.struts2.views.jsp.ComponentTagSupport;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * Append a list of iterators. The values of the iterators will be merged
+ * into one iterator.
+ *
+ * @see MergeIterator
+ * @see org.apache.struts2.util.MergeIteratorFilter
+ */
+public class MergeIteratorTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = 4999729472466011218L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new MergeIterator(stack);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/SortIteratorTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/SortIteratorTag.java
new file mode 100644
index 000000000..2c7f6e65b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/SortIteratorTag.java
@@ -0,0 +1,152 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.iterator;
+
+import java.util.Comparator;
+
+import javax.servlet.jsp.JspException;
+
+import org.apache.struts2.util.MakeIterator;
+import org.apache.struts2.util.SortIteratorFilter;
+import org.apache.struts2.views.jsp.StrutsBodyTagSupport;
+
+
+/**
+ *
+ *
+ * NOTE: JSP-TAG
+ *
+ * A Tag that sorts a List using a Comparator both passed in as the tag attribute.
+ * If 'id' attribute is specified, the sorted list will be placed into the PageContext
+ * attribute using the key specified by 'id'. The sorted list will ALWAYS be
+ * pushed into the stack and poped at the end of this tag.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * id (String) - if specified, the sorted iterator will be place with this id under page context
+ * source (Object) - the source for the sort to take place (should be iteratable) else JspException will be thrown
+ * comparator* (Object) - the comparator used to do sorting (should be a type of Comparator or its decendent) else JspException will be thrown
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * USAGE 1:
+ * <s:sort comparator="myComparator" source="myList">
+ * <s:iterator>
+ * <!-- do something with each sorted elements -->
+ * <s:property value="..." />
+ * </s:iterator>
+ * </s:sort>
+ *
+ * USAGE 2:
+ * <s:sort id="mySortedList" comparator="myComparator" source="myList" />
+ *
+ * <%
+ * Iterator sortedIterator = (Iterator) pageContext.getAttribute("mySortedList");
+ * for (Iterator i = sortedIterator; i.hasNext(); ) {
+ * // do something with each of the sorted elements
+ * }
+ * %>
+ *
+ *
+ *
+ *
+ *
+ * @see org.apache.struts2.util.SortIteratorFilter
+ *
+ * @s.tag name="sort" tld-body-content="JSP"
+ * description="Sort a List using a Comparator both passed in as the tag attribute."
+ */
+public class SortIteratorTag extends StrutsBodyTagSupport {
+
+ private static final long serialVersionUID = -7835719609764092235L;
+
+ String comparatorAttr;
+ String sourceAttr;
+
+ SortIteratorFilter sortIteratorFilter = null;
+
+ /**
+ * @s.tagattribute required="true" type="java.util.Comparator"
+ * description="The comparator to use"
+ */
+ public void setComparator(String comparator) {
+ comparatorAttr = comparator;
+ }
+
+ /**
+ * @s.tagattribute required="false"
+ * description="The iterable source to sort"
+ */
+ public void setSource(String source) {
+ sourceAttr = source;
+ }
+
+ public int doStartTag() throws JspException {
+ // Source
+ Object srcToSort;
+ if (sourceAttr == null) {
+ srcToSort = findValue("top");
+ } else {
+ srcToSort = findValue(sourceAttr);
+ }
+ if (! MakeIterator.isIterable(srcToSort)) { // see if source is Iteratable
+ throw new JspException("source ["+srcToSort+"] is not iteratable");
+ }
+
+ // Comparator
+ Object comparatorObj = findValue(comparatorAttr);
+ if (! (comparatorObj instanceof Comparator)) {
+ throw new JspException("comparator ["+comparatorObj+"] does not implements Comparator interface");
+ }
+ Comparator c = (Comparator) findValue(comparatorAttr);
+
+ // SortIteratorFilter
+ sortIteratorFilter = new SortIteratorFilter();
+ sortIteratorFilter.setComparator(c);
+ sortIteratorFilter.setSource(srcToSort);
+ sortIteratorFilter.execute();
+
+ // push sorted iterator into stack, so nexted tag have access to it
+ getStack().push(sortIteratorFilter);
+ if (getId() != null && getId().length() > 0) {
+ pageContext.setAttribute(getId(), sortIteratorFilter);
+ }
+
+ return EVAL_BODY_INCLUDE;
+ }
+
+ public int doEndTag() throws JspException {
+ int returnVal = super.doEndTag();
+
+ // pop sorted list from stack at the end of tag
+ getStack().pop();
+ sortIteratorFilter = null;
+
+ return returnVal;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/SubsetIteratorTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/SubsetIteratorTag.java
new file mode 100644
index 000000000..47a7b5e5f
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/iterator/SubsetIteratorTag.java
@@ -0,0 +1,288 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.iterator;
+
+import javax.servlet.jsp.JspException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.util.SubsetIteratorFilter;
+import org.apache.struts2.util.SubsetIteratorFilter.Decider;
+import org.apache.struts2.views.jsp.StrutsBodyTagSupport;
+
+
+/**
+ *
+ * NOTE: JSP-TAG
+ *
+ * A tag that takes an iterator and outputs a subset of it. It delegates to
+ * {@link org.apache.struts2.util.SubsetIteratorFilter} internally to
+ * perform the subset functionality.
+ *
+ *
+ *
+ *
+ * count (Object) - Indicate the number of entries to be in the resulting subset iterator
+ * source* (Object) - Indicate the source of which the resulting subset iterator is to be derived base on
+ * start (Object) - Indicate the starting index (eg. first entry is 0) of entries in the source to be available as the first entry in the resulting subset iterator
+ * decider (Object) - Extension to plug-in a decider to determine if that particular entry is to be included in the resulting subset iterator
+ * id (String) - Indicate the pageContext attribute id to store the resultant subset iterator in
+ *
+ *
+ *
+ *
+ *
+ *
+ * public class MySubsetTagAction extends ActionSupport {
+ * public String execute() throws Exception {
+ * l = new ArrayList();
+ * l.add(new Integer(1));
+ * l.add(new Integer(2));
+ * l.add(new Integer(3));
+ * l.add(new Integer(4));
+ * l.add(new Integer(5));
+ * return "done";
+ * }
+ *
+ *
+ * public Integer[] getMyArray() {
+ * return a;
+ * }
+ *
+ * public List getMyList() {
+ * return l;
+ * }
+ *
+ * public Decider getMyDecider() {
+ * return new Decider() {
+ * public boolean decide(Object element) throws Exception {
+ * int i = ((Integer)element).intValue();
+ * return (((i % 2) == 0)?true:false);
+ * }
+ * };
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ *
+ * <!-- s: List basic -->
+ * <s:subset source="myList">
+ * <s:iterator>
+ * <s:property />
+ * </s:iterator>
+ * </s:subset>
+ *
+ *
+ *
+ *
+ *
+ * <!-- B: List with count -->
+ * <s:subset source="myList" count="3">
+ * <s:iterator>
+ * <s:property />
+ * </s:iterator>
+ * </s:subset>
+ *
+ *
+ *
+ *
+ *
+ * <!-- C: List with start -->
+ * <s:subset source="myList" count="13" start="3">
+ * <s:iterator>
+ * <s:property />
+ * </s:iterator>
+ * </s:subset>
+ *
+ *
+ *
+ *
+ *
+ * <!-- D: List with id -->
+ * <s:subset id="mySubset" source="myList" count="13" start="3" />
+ * <%
+ * Iterator i = (Iterator) pageContext.getAttribute("mySubset");
+ * while(i.hasNext()) {
+ * %>
+ * <%=i.next() %>
+ * <% } %>
+ *
+ *
+ *
+ *
+ *
+ * <!-- D: List with Decider -->
+ * <s:subset source="myList" decider="myDecider">
+ * <s:iterator>
+ * <s:property />
+ * </s:iterator>
+ * </s:subset>
+ *
+ *
+ *
+ *
+ * @s.tag name="subset" tld-body-content="JSP"
+ * description="Takes an iterator and outputs a subset of it"
+ */
+public class SubsetIteratorTag extends StrutsBodyTagSupport {
+
+ private static final long serialVersionUID = -6252696081713080102L;
+
+ private static final Log _log = LogFactory.getLog(SubsetIteratorTag.class);
+
+ String countAttr;
+ String sourceAttr;
+ String startAttr;
+ String deciderAttr;
+
+ SubsetIteratorFilter subsetIteratorFilter = null;
+
+
+ /**
+ * @s.tagattribute required="false" type="Integer"
+ * description="Indicate the number of entries to be in the resulting subset iterator"
+ */
+ public void setCount(String count) {
+ countAttr = count;
+ }
+
+ /**
+ * @s.tagattribute required="false"
+ * description="Indicate the source of which the resulting subset iterator is to be derived base on"
+ */
+ public void setSource(String source) {
+ sourceAttr = source;
+ }
+
+ /**
+ * @s.tagattribute required="false" type="Integer"
+ * description="Indicate the starting index (eg. first entry is 0) of entries in the source to be available as the first entry in the resulting subset iterator"
+ */
+ public void setStart(String start) {
+ startAttr = start;
+ }
+
+ /**
+ * @s.tagattribute required="false" type="org.apache.struts2.util.SubsetIteratorFilter.Decider"
+ * description="Extension to plug-in a decider to determine if that particular entry is to be included in the resulting subset iterator"
+ */
+ public void setDecider(String decider) {
+ deciderAttr = decider;
+ }
+
+
+ public int doStartTag() throws JspException {
+
+ // source
+ Object source = null;
+ if (sourceAttr == null && sourceAttr.length() <= 0) {
+ source = findValue("top");
+ } else {
+ source = findValue(sourceAttr);
+ }
+
+ // count
+ int count = -1;
+ if (countAttr != null && countAttr.length() > 0) {
+ Object countObj = findValue(countAttr);
+ if (countObj instanceof Integer) {
+ count = ((Integer)countObj).intValue();
+ }
+ else if (countObj instanceof Float) {
+ count = ((Float)countObj).intValue();
+ }
+ else if (countObj instanceof Long) {
+ count = ((Long)countObj).intValue();
+ }
+ else if (countObj instanceof Double) {
+ count = ((Long)countObj).intValue();
+ }
+ else if (countObj instanceof String) {
+ try {
+ count = Integer.parseInt((String)countObj);
+ }
+ catch(NumberFormatException e) {
+ _log.warn("unable to convert count attribute ["+countObj+"] to number, ignore count attribute", e);
+ }
+ }
+ }
+
+ // start
+ int start = 0;
+ if (startAttr != null && startAttr.length() > 0) {
+ Object startObj = findValue(startAttr);
+ if (startObj instanceof Integer) {
+ start = ((Integer)startObj).intValue();
+ }
+ else if (startObj instanceof Float) {
+ start = ((Float)startObj).intValue();
+ }
+ else if (startObj instanceof Long) {
+ start = ((Long)startObj).intValue();
+ }
+ else if (startObj instanceof Double) {
+ start = ((Long)startObj).intValue();
+ }
+ else if (startObj instanceof String) {
+ try {
+ start = Integer.parseInt((String)startObj);
+ }
+ catch(NumberFormatException e) {
+ _log.warn("unable to convert count attribute ["+startObj+"] to number, ignore count attribute", e);
+ }
+ }
+ }
+
+ // decider
+ Decider decider = null;
+ if (deciderAttr != null && deciderAttr.length() > 0) {
+ Object deciderObj = findValue(deciderAttr);
+ if (! (deciderObj instanceof Decider)) {
+ throw new JspException("decider found from stack ["+deciderObj+"] does not implement "+Decider.class);
+ }
+ decider = (Decider) deciderObj;
+ }
+
+
+ subsetIteratorFilter = new SubsetIteratorFilter();
+ subsetIteratorFilter.setCount(count);
+ subsetIteratorFilter.setDecider(decider);
+ subsetIteratorFilter.setSource(source);
+ subsetIteratorFilter.setStart(start);
+ subsetIteratorFilter.execute();
+
+ getStack().push(subsetIteratorFilter);
+ if (getId() != null) {
+ pageContext.setAttribute(getId(), subsetIteratorFilter);
+ }
+
+ return EVAL_BODY_INCLUDE;
+ }
+
+ public int doEndTag() throws JspException {
+
+ getStack().pop();
+
+ subsetIteratorFilter = null;
+
+ return EVAL_PAGE;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/package.html b/trunk/core/src/main/java/org/apache/struts2/views/jsp/package.html
new file mode 100644
index 000000000..f17fa990b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/package.html
@@ -0,0 +1 @@
+Struts's JSP tag library.
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractClosingTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractClosingTag.java
new file mode 100644
index 000000000..f6feec829
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractClosingTag.java
@@ -0,0 +1,36 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import org.apache.struts2.components.ClosingUIBean;
+
+/**
+ */
+public abstract class AbstractClosingTag extends AbstractUITag {
+ protected String openTemplate;
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ((ClosingUIBean) component).setOpenTemplate(openTemplate);
+ }
+
+ public void setOpenTemplate(String openTemplate) {
+ this.openTemplate = openTemplate;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractDoubleListTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractDoubleListTag.java
new file mode 100644
index 000000000..26d3d1c31
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractDoubleListTag.java
@@ -0,0 +1,369 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import org.apache.struts2.components.DoubleListUIBean;
+
+
+/**
+ */
+public abstract class AbstractDoubleListTag extends AbstractRequiredListTag {
+
+ protected String doubleList;
+ protected String doubleListKey;
+ protected String doubleListValue;
+ protected String doubleName;
+ protected String doubleValue;
+ protected String formName;
+
+ protected String emptyOption;
+ protected String headerKey;
+ protected String headerValue;
+ protected String multiple;
+ protected String size;
+
+ protected String doubleId;
+ protected String doubleDisabled;
+ protected String doubleMultiple;
+ protected String doubleSize;
+ protected String doubleHeaderKey;
+ protected String doubleHeaderValue;
+ protected String doubleEmptyOption;
+
+ protected String doubleCssClass;
+ protected String doubleCssStyle;
+
+ protected String doubleOnclick;
+ protected String doubleOndblclick;
+ protected String doubleOnmousedown;
+ protected String doubleOnmouseup;
+ protected String doubleOnmouseover;
+ protected String doubleOnmousemove;
+ protected String doubleOnmouseout;
+ protected String doubleOnfocus;
+ protected String doubleOnblur;
+ protected String doubleOnkeypress;
+ protected String doubleOnkeydown;
+ protected String doubleOnkeyup;
+ protected String doubleOnselect;
+ protected String doubleOnchange;
+
+ protected String doubleAccesskey;
+
+ protected void populateParams() {
+ super.populateParams();
+
+ DoubleListUIBean bean = ((DoubleListUIBean) this.component);
+ bean.setDoubleList(doubleList);
+ bean.setDoubleListKey(doubleListKey);
+ bean.setDoubleListValue(doubleListValue);
+ bean.setDoubleName(doubleName);
+ bean.setDoubleValue(doubleValue);
+ bean.setFormName(formName);
+
+ bean.setDoubleId(doubleId);
+ bean.setDoubleDisabled(doubleDisabled);
+ bean.setDoubleMultiple(doubleMultiple);
+ bean.setDoubleSize(doubleSize);
+ bean.setDoubleHeaderKey(doubleHeaderKey);
+ bean.setDoubleHeaderValue(doubleHeaderValue);
+ bean.setDoubleEmptyOption(doubleEmptyOption);
+
+ bean.setDoubleCssClass(doubleCssClass);
+ bean.setDoubleCssStyle(doubleCssStyle);
+
+ bean.setDoubleOnclick(doubleOnclick);
+ bean.setDoubleOndblclick(doubleOndblclick);
+ bean.setDoubleOnmousedown(doubleOnmousedown);
+ bean.setDoubleOnmouseup(doubleOnmouseup);
+ bean.setDoubleOnmouseover(doubleOnmouseover);
+ bean.setDoubleOnmousemove(doubleOnmousemove);
+ bean.setDoubleOnmouseout(doubleOnmouseout);
+ bean.setDoubleOnfocus(doubleOnfocus);
+ bean.setDoubleOnblur(doubleOnblur);
+ bean.setDoubleOnkeypress(doubleOnkeypress);
+ bean.setDoubleOnkeydown(doubleOnkeydown);
+ bean.setDoubleOnkeyup(doubleOnkeyup);
+ bean.setDoubleOnselect(doubleOnselect);
+ bean.setDoubleOnchange(doubleOnchange);
+
+ bean.setDoubleAccesskey(doubleAccesskey);
+
+ bean.setEmptyOption(emptyOption);
+ bean.setHeaderKey(headerKey);
+ bean.setHeaderValue(headerValue);
+ bean.setMultiple(multiple);
+ bean.setSize(size);
+ }
+
+ public void setDoubleList(String list) {
+ this.doubleList = list;
+ }
+
+ public void setDoubleListKey(String listKey) {
+ this.doubleListKey = listKey;
+ }
+
+ public void setDoubleListValue(String listValue) {
+ this.doubleListValue = listValue;
+ }
+
+ public void setDoubleName(String aName) {
+ doubleName = aName;
+ }
+
+ public void setDoubleValue(String doubleValue) {
+ this.doubleValue = doubleValue;
+ }
+
+ public void setFormName(String formName) {
+ this.formName = formName;
+ }
+
+ public String getDoubleCssClass() {
+ return doubleCssClass;
+ }
+
+ public void setDoubleCssClass(String doubleCssClass) {
+ this.doubleCssClass = doubleCssClass;
+ }
+
+ public String getDoubleCssStyle() {
+ return doubleCssStyle;
+ }
+
+ public void setDoubleCssStyle(String doubleCssStyle) {
+ this.doubleCssStyle = doubleCssStyle;
+ }
+
+ public String getDoubleDisabled() {
+ return doubleDisabled;
+ }
+
+ public void setDoubleDisabled(String doubleDisabled) {
+ this.doubleDisabled = doubleDisabled;
+ }
+
+ public String getDoubleEmptyOption() {
+ return doubleEmptyOption;
+ }
+
+ public void setDoubleEmptyOption(String doubleEmptyOption) {
+ this.doubleEmptyOption = doubleEmptyOption;
+ }
+
+ public String getDoubleHeaderKey() {
+ return doubleHeaderKey;
+ }
+
+ public void setDoubleHeaderKey(String doubleHeaderKey) {
+ this.doubleHeaderKey = doubleHeaderKey;
+ }
+
+ public String getDoubleHeaderValue() {
+ return doubleHeaderValue;
+ }
+
+ public void setDoubleHeaderValue(String doubleHeaderValue) {
+ this.doubleHeaderValue = doubleHeaderValue;
+ }
+
+ public String getDoubleId() {
+ return doubleId;
+ }
+
+ public void setDoubleId(String doubleId) {
+ this.doubleId = doubleId;
+ }
+
+ public String getDoubleMultiple() {
+ return doubleMultiple;
+ }
+
+ public void setDoubleMultiple(String doubleMultiple) {
+ this.doubleMultiple = doubleMultiple;
+ }
+
+ public String getDoubleOnblur() {
+ return doubleOnblur;
+ }
+
+ public void setDoubleOnblur(String doubleOnblur) {
+ this.doubleOnblur = doubleOnblur;
+ }
+
+ public String getDoubleOnchange() {
+ return doubleOnchange;
+ }
+
+ public void setDoubleOnchange(String doubleOnchange) {
+ this.doubleOnchange = doubleOnchange;
+ }
+
+ public String getDoubleOnclick() {
+ return doubleOnclick;
+ }
+
+ public void setDoubleOnclick(String doubleOnclick) {
+ this.doubleOnclick = doubleOnclick;
+ }
+
+ public String getDoubleOndblclick() {
+ return doubleOndblclick;
+ }
+
+ public void setDoubleOndblclick(String doubleOndblclick) {
+ this.doubleOndblclick = doubleOndblclick;
+ }
+
+ public String getDoubleOnfocus() {
+ return doubleOnfocus;
+ }
+
+ public void setDoubleOnfocus(String doubleOnfocus) {
+ this.doubleOnfocus = doubleOnfocus;
+ }
+
+ public String getDoubleOnkeydown() {
+ return doubleOnkeydown;
+ }
+
+ public void setDoubleOnkeydown(String doubleOnkeydown) {
+ this.doubleOnkeydown = doubleOnkeydown;
+ }
+
+ public String getDoubleOnkeypress() {
+ return doubleOnkeypress;
+ }
+
+ public void setDoubleOnkeypress(String doubleOnkeypress) {
+ this.doubleOnkeypress = doubleOnkeypress;
+ }
+
+ public String getDoubleOnkeyup() {
+ return doubleOnkeyup;
+ }
+
+ public void setDoubleOnkeyup(String doubleOnkeyup) {
+ this.doubleOnkeyup = doubleOnkeyup;
+ }
+
+ public String getDoubleOnmousedown() {
+ return doubleOnmousedown;
+ }
+
+ public void setDoubleOnmousedown(String doubleOnmousedown) {
+ this.doubleOnmousedown = doubleOnmousedown;
+ }
+
+ public String getDoubleOnmousemove() {
+ return doubleOnmousemove;
+ }
+
+ public void setDoubleOnmousemove(String doubleOnmousemove) {
+ this.doubleOnmousemove = doubleOnmousemove;
+ }
+
+ public String getDoubleOnmouseout() {
+ return doubleOnmouseout;
+ }
+
+ public void setDoubleOnmouseout(String doubleOnmouseout) {
+ this.doubleOnmouseout = doubleOnmouseout;
+ }
+
+ public String getDoubleOnmouseover() {
+ return doubleOnmouseover;
+ }
+
+ public void setDoubleOnmouseover(String doubleOnmouseover) {
+ this.doubleOnmouseover = doubleOnmouseover;
+ }
+
+ public String getDoubleOnmouseup() {
+ return doubleOnmouseup;
+ }
+
+ public void setDoubleOnmouseup(String doubleOnmouseup) {
+ this.doubleOnmouseup = doubleOnmouseup;
+ }
+
+ public String getDoubleOnselect() {
+ return doubleOnselect;
+ }
+
+ public void setDoubleOnselect(String doubleOnselect) {
+ this.doubleOnselect = doubleOnselect;
+ }
+
+ public String getDoubleSize() {
+ return doubleSize;
+ }
+
+ public void setDoubleSize(String doubleSize) {
+ this.doubleSize = doubleSize;
+ }
+
+ public String getDoubleList() {
+ return doubleList;
+ }
+
+ public String getDoubleListKey() {
+ return doubleListKey;
+ }
+
+ public String getDoubleListValue() {
+ return doubleListValue;
+ }
+
+ public String getDoubleName() {
+ return doubleName;
+ }
+
+ public String getDoubleValue() {
+ return doubleValue;
+ }
+
+ public String getFormName() {
+ return formName;
+ }
+
+ public void setEmptyOption(String emptyOption) {
+ this.emptyOption = emptyOption;
+ }
+
+ public void setHeaderKey(String headerKey) {
+ this.headerKey = headerKey;
+ }
+
+ public void setHeaderValue(String headerValue) {
+ this.headerValue = headerValue;
+ }
+
+ public void setMultiple(String multiple) {
+ this.multiple = multiple;
+ }
+
+ public void setSize(String size) {
+ this.size = size;
+ }
+
+ public void setDoubleAccesskey(String doubleAccesskey) {
+ this.doubleAccesskey = doubleAccesskey;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractListTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractListTag.java
new file mode 100644
index 000000000..372f93ecf
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractListTag.java
@@ -0,0 +1,49 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import org.apache.struts2.components.ListUIBean;
+
+/**
+ */
+public abstract class AbstractListTag extends AbstractUITag {
+ protected String list;
+ protected String listKey;
+ protected String listValue;
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ListUIBean listUIBean = ((ListUIBean) component);
+ listUIBean.setList(list);
+ listUIBean.setListKey(listKey);
+ listUIBean.setListValue(listValue);
+ }
+
+ public void setList(String list) {
+ this.list = list;
+ }
+
+ public void setListKey(String listKey) {
+ this.listKey = listKey;
+ }
+
+ public void setListValue(String listValue) {
+ this.listValue = listValue;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractRequiredListTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractRequiredListTag.java
new file mode 100644
index 000000000..db6ed4afe
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractRequiredListTag.java
@@ -0,0 +1,34 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+
+import org.apache.struts2.components.ListUIBean;
+
+/**
+ */
+public abstract class AbstractRequiredListTag extends AbstractListTag {
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ListUIBean listUIBean = (ListUIBean) component;
+ listUIBean.setThrowExceptionOnNullValueAttribute(true);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractUITag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractUITag.java
new file mode 100644
index 000000000..cf9dbe48e
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractUITag.java
@@ -0,0 +1,225 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import org.apache.struts2.components.UIBean;
+import org.apache.struts2.views.jsp.ComponentTagSupport;
+
+
+/**
+ * Abstract base class for all UI tags.
+ *
+ */
+public abstract class AbstractUITag extends ComponentTagSupport {
+ protected String cssClass;
+ protected String cssStyle;
+ protected String title;
+ protected String disabled;
+ protected String label;
+ protected String labelPosition;
+ protected String requiredposition;
+ protected String name;
+ protected String required;
+ protected String tabindex;
+ protected String value;
+ protected String template;
+ protected String theme;
+ protected String templateDir;
+ protected String onclick;
+ protected String ondblclick;
+ protected String onmousedown;
+ protected String onmouseup;
+ protected String onmouseover;
+ protected String onmousemove;
+ protected String onmouseout;
+ protected String onfocus;
+ protected String onblur;
+ protected String onkeypress;
+ protected String onkeydown;
+ protected String onkeyup;
+ protected String onselect;
+ protected String onchange;
+ protected String accesskey;
+
+ // tooltip attributes
+ protected String tooltip;
+ protected String tooltipConfig;
+
+
+ protected void populateParams() {
+ super.populateParams();
+
+ UIBean uiBean = (UIBean) component;
+ uiBean.setCssClass(cssClass);
+ uiBean.setCssClass(cssClass);
+ uiBean.setCssStyle(cssStyle);
+ uiBean.setTitle(title);
+ uiBean.setDisabled(disabled);
+ uiBean.setLabel(label);
+ uiBean.setLabelposition(labelPosition);
+ uiBean.setRequiredposition(requiredposition);
+ uiBean.setName(name);
+ uiBean.setRequired(required);
+ uiBean.setTabindex(tabindex);
+ uiBean.setValue(value);
+ uiBean.setTemplate(template);
+ uiBean.setTheme(theme);
+ uiBean.setTemplateDir(templateDir);
+ uiBean.setOnclick(onclick);
+ uiBean.setOndblclick(ondblclick);
+ uiBean.setOnmousedown(onmousedown);
+ uiBean.setOnmouseup(onmouseup);
+ uiBean.setOnmouseover(onmouseover);
+ uiBean.setOnmousemove(onmousemove);
+ uiBean.setOnmouseout(onmouseout);
+ uiBean.setOnfocus(onfocus);
+ uiBean.setOnblur(onblur);
+ uiBean.setOnkeypress(onkeypress);
+ uiBean.setOnkeydown(onkeydown);
+ uiBean.setOnkeyup(onkeyup);
+ uiBean.setOnselect(onselect);
+ uiBean.setOnchange(onchange);
+ uiBean.setTooltip(tooltip);
+ uiBean.setTooltipConfig(tooltipConfig);
+ uiBean.setAccesskey(accesskey);
+ }
+
+ public void setCssClass(String cssClass) {
+ this.cssClass = cssClass;
+ }
+
+ public void setCssStyle(String cssStyle) {
+ this.cssStyle = cssStyle;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setDisabled(String disabled) {
+ this.disabled = disabled;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public void setLabelposition(String labelPosition) {
+ this.labelPosition = labelPosition;
+ }
+
+ public void setRequiredposition(String requiredPosition) {
+ this.requiredposition = requiredPosition;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setRequired(String required) {
+ this.required = required;
+ }
+
+ public void setTabindex(String tabindex) {
+ this.tabindex = tabindex;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public void setTemplateDir(String templateDir) {
+ this.templateDir = templateDir;
+ }
+
+ public void setTemplate(String template) {
+ this.template = template;
+ }
+
+ public void setTheme(String theme) {
+ this.theme = theme;
+ }
+
+ public void setOnclick(String onclick) {
+ this.onclick = onclick;
+ }
+
+ public void setOndblclick(String ondblclick) {
+ this.ondblclick = ondblclick;
+ }
+
+ public void setOnmousedown(String onmousedown) {
+ this.onmousedown = onmousedown;
+ }
+
+ public void setOnmouseup(String onmouseup) {
+ this.onmouseup = onmouseup;
+ }
+
+ public void setOnmouseover(String onmouseover) {
+ this.onmouseover = onmouseover;
+ }
+
+ public void setOnmousemove(String onmousemove) {
+ this.onmousemove = onmousemove;
+ }
+
+ public void setOnmouseout(String onmouseout) {
+ this.onmouseout = onmouseout;
+ }
+
+ public void setOnfocus(String onfocus) {
+ this.onfocus = onfocus;
+ }
+
+ public void setOnblur(String onblur) {
+ this.onblur = onblur;
+ }
+
+ public void setOnkeypress(String onkeypress) {
+ this.onkeypress = onkeypress;
+ }
+
+ public void setOnkeydown(String onkeydown) {
+ this.onkeydown = onkeydown;
+ }
+
+ public void setOnkeyup(String onkeyup) {
+ this.onkeyup = onkeyup;
+ }
+
+ public void setOnselect(String onselect) {
+ this.onselect = onselect;
+ }
+
+ public void setOnchange(String onchange) {
+ this.onchange = onchange;
+ }
+
+ public void setTooltip(String tooltip) {
+ this.tooltip = tooltip;
+ }
+
+ public void setTooltipConfig(String tooltipConfig) {
+ this.tooltipConfig = tooltipConfig;
+ }
+
+ public void setAccesskey(String accesskey) {
+ this.accesskey = accesskey;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ActionErrorTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ActionErrorTag.java
new file mode 100644
index 000000000..020229830
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ActionErrorTag.java
@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ActionError;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * ActionError Tag.
+ *
+ */
+public class ActionErrorTag extends AbstractUITag {
+
+ private static final long serialVersionUID = -3710234378022378639L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new ActionError(stack, req, res);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ActionMessageTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ActionMessageTag.java
new file mode 100644
index 000000000..54d6bd569
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ActionMessageTag.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ActionMessage;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * ActionMessage Tag.
+ *
+ */
+public class ActionMessageTag extends AbstractUITag {
+
+ private static final long serialVersionUID = 243396927554182506L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new ActionMessage(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AnchorTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AnchorTag.java
new file mode 100644
index 000000000..79f88b826
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/AnchorTag.java
@@ -0,0 +1,83 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Anchor;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Anchor
+ */
+public class AnchorTag extends AbstractClosingTag {
+
+ private static final long serialVersionUID = -1034616578492431113L;
+
+ protected String href;
+ protected String errorText;
+ protected String showErrorTransportText;
+ protected String notifyTopics;
+ protected String afterLoading;
+ protected String preInvokeJS;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Anchor(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ Anchor link = (Anchor) component;
+
+ link.setHref(href);
+ link.setErrorText(errorText);
+ link.setShowErrorTransportText(showErrorTransportText);
+ link.setNotifyTopics(notifyTopics);
+ link.setAfterLoading(afterLoading);
+ link.setPreInvokeJS(preInvokeJS);
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+
+ public void setErrorText(String errorText) {
+ this.errorText = errorText;
+ }
+
+ public void setShowErrorTransportText(String showErrorTransportText) {
+ this.showErrorTransportText = showErrorTransportText;
+ }
+
+ public void setNotifyTopics(String notifyTopics) {
+ this.notifyTopics = notifyTopics;
+ }
+
+ public void setAfterLoading(String afterLoading) {
+ this.afterLoading = afterLoading;
+ }
+
+ public void setPreInvokeJS(String preInvokeJS) {
+ this.preInvokeJS = preInvokeJS;
+ }
+}
+
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/CheckboxListTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/CheckboxListTag.java
new file mode 100644
index 000000000..610856829
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/CheckboxListTag.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.CheckboxList;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see CheckboxList
+ */
+public class CheckboxListTag extends AbstractRequiredListTag {
+
+ private static final long serialVersionUID = 4023034029558150010L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new CheckboxList(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/CheckboxTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/CheckboxTag.java
new file mode 100644
index 000000000..4bc493ca9
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/CheckboxTag.java
@@ -0,0 +1,51 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Checkbox;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Checkbox
+ */
+public class CheckboxTag extends AbstractUITag {
+
+ private static final long serialVersionUID = -350752809266337636L;
+
+ protected String fieldValue;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Checkbox(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ((Checkbox) component).setFieldValue(fieldValue);
+ }
+
+ public void setFieldValue(String aValue) {
+ this.fieldValue = aValue;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ComboBoxTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ComboBoxTag.java
new file mode 100644
index 000000000..c699c0a39
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ComboBoxTag.java
@@ -0,0 +1,80 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ComboBox;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ComboBox
+ */
+public class ComboBoxTag extends TextFieldTag {
+
+ private static final long serialVersionUID = 3509392460170385605L;
+
+ protected String list;
+ protected String listKey;
+ protected String listValue;
+ protected String headerKey;
+ protected String headerValue;
+ protected String emptyOption;
+
+ public void setEmptyOption(String emptyOption) {
+ this.emptyOption = emptyOption;
+ }
+
+ public void setHeaderKey(String headerKey) {
+ this.headerKey = headerKey;
+ }
+
+ public void setHeaderValue(String headerValue) {
+ this.headerValue = headerValue;
+ }
+
+ public void setListKey(String listKey) {
+ this.listKey = listKey;
+ }
+
+ public void setListValue(String listValue) {
+ this.listValue = listValue;
+ }
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new ComboBox(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ((ComboBox) component).setList(list);
+ ((ComboBox) component).setListKey(listKey);
+ ((ComboBox) component).setListValue(listValue);
+ ((ComboBox) component).setHeaderKey(headerKey);
+ ((ComboBox) component).setHeaderValue(headerValue);
+ ((ComboBox) component).setEmptyOption(emptyOption);
+ }
+
+ public void setList(String list) {
+ this.list = list;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ComponentTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ComponentTag.java
new file mode 100644
index 000000000..4aa464fba
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ComponentTag.java
@@ -0,0 +1,38 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.GenericUIBean;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see GenericUIBean
+ */
+public class ComponentTag extends AbstractUITag {
+
+ private static final long serialVersionUID = 5448365363044104731L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new GenericUIBean(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DatePickerTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DatePickerTag.java
new file mode 100644
index 000000000..e10d92475
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DatePickerTag.java
@@ -0,0 +1,71 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.DatePicker;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see DatePicker
+ */
+public class DatePickerTag extends TextFieldTag {
+
+ private static final long serialVersionUID = 4054114507143447232L;
+
+ protected String format;
+ protected String dateIconPath;
+ protected String templatePath;
+ protected String templateCssPath;
+
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new DatePicker(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ final DatePicker datePicker = (DatePicker) component;
+ datePicker.setFormat(format);
+ datePicker.setDateIconPath(dateIconPath);
+ datePicker.setTemplatePath(templatePath);
+ datePicker.setTemplateCssPath(templateCssPath);
+ }
+
+ public void setFormat(String format) {
+ this.format = format;
+ }
+
+ public void setDateIconPath(String dateIconPath) {
+ this.dateIconPath = dateIconPath;
+ }
+
+ public void setTemplatePath(String templatePath) {
+ this.templatePath = templatePath;
+ }
+
+ public void setTemplateCssPath(String templateCsspath) {
+ this.templateCssPath = templateCsspath;
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DebugTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DebugTag.java
new file mode 100644
index 000000000..a49391c44
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DebugTag.java
@@ -0,0 +1,19 @@
+package org.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Debug;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+public class DebugTag extends AbstractUITag {
+
+ private static final long serialVersionUID = 3487684841317160628L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Debug(stack, req, res);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DivTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DivTag.java
new file mode 100644
index 000000000..20ce6fcda
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DivTag.java
@@ -0,0 +1,90 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Div;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+public class DivTag extends AbstractClosingTag {
+
+ private static final long serialVersionUID = 5309231035916461758L;
+
+ protected String href;
+ protected String updateFreq;
+ protected String delay="1";
+ protected String loadingText;
+ protected String errorText;
+ protected String showErrorTransportText;
+ protected String listenTopics;
+ protected String afterLoading;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Div(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ Div div = (Div) component;
+ div.setHref(href);
+ div.setUpdateFreq(updateFreq);
+ div.setDelay(delay);
+ div.setLoadingText(loadingText);
+ div.setErrorText(errorText);
+ div.setShowErrorTransportText(showErrorTransportText);
+ div.setListenTopics(listenTopics);
+ div.setAfterLoading(afterLoading);
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+
+ public void setUpdateFreq(String updateFreq) {
+ this.updateFreq = updateFreq;
+ }
+
+ public void setDelay(String delay) {
+ this.delay = delay;
+ }
+
+ public void setLoadingText(String loadingText) {
+ this.loadingText = loadingText;
+ }
+
+ public void setErrorText(String errorText) {
+ this.errorText = errorText;
+ }
+
+ public void setShowErrorTransportText(String showErrorTransportText) {
+ this.showErrorTransportText = showErrorTransportText;
+ }
+
+ public void setListenTopics(String listenTopics) {
+ this.listenTopics = listenTopics;
+ }
+
+ public void setAfterLoading(String afterLoading) {
+ this.afterLoading = afterLoading;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DoubleSelectTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DoubleSelectTag.java
new file mode 100644
index 000000000..832ecae25
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/DoubleSelectTag.java
@@ -0,0 +1,50 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.DoubleSelect;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see DoubleSelect
+ */
+public class DoubleSelectTag extends AbstractDoubleListTag {
+
+ private static final long serialVersionUID = 7426011596359509386L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new DoubleSelect(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ DoubleSelect doubleSelect = ((DoubleSelect) component);
+ doubleSelect.setEmptyOption(emptyOption);
+ doubleSelect.setHeaderKey(headerKey);
+ doubleSelect.setHeaderValue(headerValue);
+ doubleSelect.setMultiple(multiple);
+ doubleSelect.setSize(size);
+
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/FieldErrorTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/FieldErrorTag.java
new file mode 100644
index 000000000..ce4e86f01
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/FieldErrorTag.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.FieldError;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * FieldError Tag.
+ */
+public class FieldErrorTag extends AbstractUITag {
+
+ private static final long serialVersionUID = -182532967507726323L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new FieldError(stack, req, res);
+ }
+}
+
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/FileTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/FileTag.java
new file mode 100644
index 000000000..75be076b4
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/FileTag.java
@@ -0,0 +1,58 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.File;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see File
+ */
+public class FileTag extends AbstractUITag {
+
+ private static final long serialVersionUID = -2154950640215144864L;
+
+ protected String accept;
+ protected String size;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new File(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ File file = ((File) component);
+ file.setAccept(accept);
+ file.setSize(size);
+ }
+
+ public void setAccept(String accept) {
+ this.accept = accept;
+ }
+
+ public void setSize(String size) {
+ this.size = size;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/FormTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/FormTag.java
new file mode 100644
index 000000000..82aa83333
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/FormTag.java
@@ -0,0 +1,106 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Form;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Form
+ */
+public class FormTag extends AbstractClosingTag {
+
+ private static final long serialVersionUID = 2792301046860819658L;
+
+ protected String action;
+ protected String target;
+ protected String enctype;
+ protected String method;
+ protected String namespace;
+ protected String validate;
+ protected String onsubmit;
+ protected String portletMode;
+ protected String windowState;
+ protected String acceptcharset;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Form(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+ Form form = ((Form) component);
+ form.setAction(action);
+ form.setTarget(target);
+ form.setEnctype(enctype);
+ form.setMethod(method);
+ form.setNamespace(namespace);
+ form.setValidate(validate);
+ form.setOnsubmit(onsubmit);
+ form.setPortletMode(portletMode);
+ form.setWindowState(windowState);
+ form.setAcceptcharset(acceptcharset);
+ }
+
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
+
+ public void setEnctype(String enctype) {
+ this.enctype = enctype;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public void setValidate(String validate) {
+ this.validate = validate;
+ }
+
+ public void setOnsubmit(String onsubmit) {
+ this.onsubmit = onsubmit;
+ }
+
+ public void setPortletMode(String portletMode) {
+ this.portletMode = portletMode;
+ }
+
+ public void setWindowState(String windowState) {
+ this.windowState = windowState;
+ }
+
+ public void setAcceptcharset(String acceptcharset) {
+ this.acceptcharset = acceptcharset;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/HeadTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/HeadTag.java
new file mode 100644
index 000000000..e7af7dbfd
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/HeadTag.java
@@ -0,0 +1,63 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Head;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Head
+ */
+public class HeadTag extends AbstractUITag {
+
+ private static final long serialVersionUID = 6876765769175246030L;
+
+ private String calendarcss;
+ private String debug;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Head(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+ if (calendarcss != null) {
+ ((Head) component).setCalendarcss(calendarcss);
+ }
+ if (debug != null) {
+ ((Head) component).setDebug(Boolean.valueOf(debug).booleanValue());
+ }
+ }
+
+ public String getCalendarcss() {
+ return calendarcss;
+ }
+
+ public void setCalendarcss(String calendarcss) {
+ this.calendarcss = calendarcss;
+ }
+
+ public void setDebug(String debug) {
+ this.debug = debug;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/HiddenTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/HiddenTag.java
new file mode 100644
index 000000000..45a9aa61c
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/HiddenTag.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Hidden;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Hidden
+ */
+public class HiddenTag extends AbstractUITag {
+
+ private static final long serialVersionUID = -1124367972048371675L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Hidden(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/LabelTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/LabelTag.java
new file mode 100644
index 000000000..29052fd82
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/LabelTag.java
@@ -0,0 +1,51 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Label;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Label
+ */
+public class LabelTag extends AbstractUITag {
+
+ private static final long serialVersionUID = 4008321310097730458L;
+
+ protected String forAttr;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Label(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ((Label) component).setFor(forAttr);
+ }
+
+ public void setFor(String aFor) {
+ this.forAttr = aFor;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/OgnlTool.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/OgnlTool.java
new file mode 100644
index 000000000..f94b412b0
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/OgnlTool.java
@@ -0,0 +1,45 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import ognl.Ognl;
+import ognl.OgnlException;
+
+import com.opensymphony.xwork2.util.OgnlUtil;
+
+
+/**
+ */
+public class OgnlTool {
+ private static OgnlTool instance = new OgnlTool();
+
+ private OgnlTool() {
+ }
+
+ public static OgnlTool getInstance() {
+ return instance;
+ }
+
+ public Object findValue(String expr, Object context) {
+ try {
+ return Ognl.getValue(OgnlUtil.compile(expr), context);
+ } catch (OgnlException e) {
+ return null;
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/OptGroupTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/OptGroupTag.java
new file mode 100644
index 000000000..9578f8d02
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/OptGroupTag.java
@@ -0,0 +1,76 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.OptGroup;
+import org.apache.struts2.views.jsp.ComponentTagSupport;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ *
+ */
+public class OptGroupTag extends ComponentTagSupport {
+
+ private static final long serialVersionUID = 7367401003498678762L;
+
+ protected String list;
+ protected String label;
+ protected String disabled;
+ protected String listKey;
+ protected String listValue;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new OptGroup(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ OptGroup optGroup = (OptGroup) component;
+ optGroup.setList(list);
+ optGroup.setLabel(label);
+ optGroup.setDisabled(disabled);
+ optGroup.setListKey(listKey);
+ optGroup.setListValue(listValue);
+ }
+
+ public void setList(String list) {
+ this.list = list;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public void setDisabled(String disabled) {
+ this.disabled = disabled;
+ }
+
+ public void setListKey(String listKey) {
+ this.listKey = listKey;
+ }
+
+ public void setListValue(String listValue) {
+ this.listValue = listValue;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/OptionTransferSelectTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/OptionTransferSelectTag.java
new file mode 100644
index 000000000..c6e41640c
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/OptionTransferSelectTag.java
@@ -0,0 +1,225 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.OptionTransferSelect;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * OptionTransferSelect jsp tag.
+ */
+public class OptionTransferSelectTag extends AbstractDoubleListTag {
+
+ private static final long serialVersionUID = 250474334495763536L;
+
+ protected String allowAddToLeft;
+ protected String allowAddToRight;
+ protected String allowAddAllToLeft;
+ protected String allowAddAllToRight;
+ protected String allowSelectAll;
+ protected String allowUpDownOnLeft;
+ protected String allowUpDownOnRight;
+
+ protected String leftTitle;
+ protected String rightTitle;
+
+ protected String buttonCssClass;
+ protected String buttonCssStyle;
+
+ protected String addToLeftLabel;
+ protected String addToRightLabel;
+ protected String addAllToLeftLabel;
+ protected String addAllToRightLabel;
+ protected String selectAllLabel;
+ protected String leftUpLabel;
+ protected String leftDownLabel;
+ protected String rightUpLabel;
+ protected String rightDownLabel;
+
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new OptionTransferSelect(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ OptionTransferSelect optionTransferSelect = (OptionTransferSelect) component;
+ optionTransferSelect.setAllowAddToLeft(allowAddToLeft);
+ optionTransferSelect.setAllowAddToRight(allowAddToRight);
+ optionTransferSelect.setAllowAddAllToLeft(allowAddAllToLeft);
+ optionTransferSelect.setAllowAddAllToRight(allowAddAllToRight);
+ optionTransferSelect.setAllowSelectAll(allowSelectAll);
+ optionTransferSelect.setAllowUpDownOnLeft(allowUpDownOnLeft);
+ optionTransferSelect.setAllowUpDownOnRight(allowUpDownOnRight);
+
+ optionTransferSelect.setAddToLeftLabel(addToLeftLabel);
+ optionTransferSelect.setAddToRightLabel(addToRightLabel);
+ optionTransferSelect.setAddAllToLeftLabel(addAllToLeftLabel);
+ optionTransferSelect.setAddAllToRightLabel(addAllToRightLabel);
+ optionTransferSelect.setSelectAllLabel(selectAllLabel);
+ optionTransferSelect.setLeftUpLabel(leftUpLabel);
+ optionTransferSelect.setLeftDownLabel(leftDownLabel);
+ optionTransferSelect.setRightUpLabel(rightUpLabel);
+ optionTransferSelect.setRightDownLabel(rightDownLabel);
+
+ optionTransferSelect.setButtonCssClass(buttonCssClass);
+ optionTransferSelect.setButtonCssStyle(buttonCssStyle);
+
+ optionTransferSelect.setLeftTitle(leftTitle);
+ optionTransferSelect.setRightTitle(rightTitle);
+ }
+
+
+ public String getAddAllToLeftLabel() {
+ return addAllToLeftLabel;
+ }
+
+
+ public void setAddAllToLeftLabel(String addAllToLeftLabel) {
+ this.addAllToLeftLabel = addAllToLeftLabel;
+ }
+
+
+ public String getAddAllToRightLabel() {
+ return addAllToRightLabel;
+ }
+
+
+ public void setAddAllToRightLabel(String addAllToRightLabel) {
+ this.addAllToRightLabel = addAllToRightLabel;
+ }
+
+
+ public String getAddToLeftLabel() {
+ return addToLeftLabel;
+ }
+
+
+ public void setAddToLeftLabel(String addToLeftLabel) {
+ this.addToLeftLabel = addToLeftLabel;
+ }
+
+
+ public String getAddToRightLabel() {
+ return addToRightLabel;
+ }
+
+
+ public void setAddToRightLabel(String addToRightLabel) {
+ this.addToRightLabel = addToRightLabel;
+ }
+
+
+ public String getAllowAddAllToLeft() {
+ return allowAddAllToLeft;
+ }
+
+
+ public void setAllowAddAllToLeft(String allowAddAllToLeft) {
+ this.allowAddAllToLeft = allowAddAllToLeft;
+ }
+
+
+ public String getAllowAddAllToRight() {
+ return allowAddAllToRight;
+ }
+
+
+ public void setAllowAddAllToRight(String allowAddAllToRight) {
+ this.allowAddAllToRight = allowAddAllToRight;
+ }
+
+
+ public String getAllowAddToLeft() {
+ return allowAddToLeft;
+ }
+
+
+ public void setAllowAddToLeft(String allowAddToLeft) {
+ this.allowAddToLeft = allowAddToLeft;
+ }
+
+
+ public String getAllowAddToRight() {
+ return allowAddToRight;
+ }
+
+
+ public void setAllowAddToRight(String allowAddToRight) {
+ this.allowAddToRight = allowAddToRight;
+ }
+
+
+ public String getLeftTitle() {
+ return leftTitle;
+ }
+
+
+ public void setLeftTitle(String leftTitle) {
+ this.leftTitle = leftTitle;
+ }
+
+
+ public String getRightTitle() {
+ return rightTitle;
+ }
+
+
+ public void setRightTitle(String rightTitle) {
+ this.rightTitle = rightTitle;
+ }
+
+
+ public void setAllowSelectAll(String allowSelectAll) {
+ this.allowSelectAll = allowSelectAll;
+ }
+
+ public String getAllowSelectAll() {
+ return this.allowSelectAll;
+ }
+
+ public void setSelectAllLabel(String selectAllLabel) {
+ this.selectAllLabel = selectAllLabel;
+ }
+
+ public String getSelectAllLabel() {
+ return this.selectAllLabel;
+ }
+
+ public void setButtonCssClass(String buttonCssId) {
+ this.buttonCssClass = buttonCssId;
+ }
+
+ public String getButtonCssClass() {
+ return buttonCssClass;
+ }
+
+ public void setButtonCssStyle(String buttonCssStyle) {
+ this.buttonCssStyle = buttonCssStyle;
+ }
+
+ public String getButtonCssStyle() {
+ return this.buttonCssStyle;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/PanelTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/PanelTag.java
new file mode 100644
index 000000000..67245f7aa
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/PanelTag.java
@@ -0,0 +1,63 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Panel;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Panel
+ */
+public class PanelTag extends DivTag {
+
+ private static final long serialVersionUID = -1698805503599998611L;
+
+ protected String tabName;
+ protected String subscribeTopicName;
+ protected String remote;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Panel(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ Panel panel = ((Panel) component);
+ panel.setTabName(tabName);
+ panel.setSubscribeTopicName(subscribeTopicName);
+ panel.setRemote(remote);
+ }
+
+ public void setTabName(String tabName) {
+ this.tabName = tabName;
+ }
+
+ public void setSubscribeTopicName(String subscribeTopicName) {
+ this.subscribeTopicName = subscribeTopicName;
+ }
+
+ public void setRemote(String remote) {
+ this.remote = remote;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/PasswordTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/PasswordTag.java
new file mode 100644
index 000000000..f6ca252a8
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/PasswordTag.java
@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Password;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Password
+ */
+public class PasswordTag extends TextFieldTag {
+
+ private static final long serialVersionUID = 6802043323617377573L;
+
+ protected String showPassword;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Password(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ ((Password) component).setShowPassword(showPassword);
+ }
+
+ public void setShow(String showPassword) {
+ this.showPassword = showPassword;
+ }
+
+ public void setShowPassword(String showPassword) {
+ this.showPassword = showPassword;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/RadioTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/RadioTag.java
new file mode 100644
index 000000000..fdc1fe9a3
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/RadioTag.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Radio;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Radio
+ */
+public class RadioTag extends AbstractRequiredListTag {
+
+ private static final long serialVersionUID = -6497403399521333624L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Radio(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ResetTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ResetTag.java
new file mode 100644
index 000000000..af5459444
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/ResetTag.java
@@ -0,0 +1,70 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Reset;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see org.apache.struts2.components.Reset
+ */
+public class ResetTag extends AbstractUITag {
+
+ private static final long serialVersionUID = 4742704832277392108L;
+
+ protected String action;
+ protected String method;
+ protected String align;
+ protected String type;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Reset(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ Reset reset = ((Reset) component);
+ reset.setAction(action);
+ reset.setMethod(method);
+ reset.setAlign(align);
+ reset.setType(type);
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public void setAlign(String align) {
+ this.align = align;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/SelectTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/SelectTag.java
new file mode 100644
index 000000000..878865cfe
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/SelectTag.java
@@ -0,0 +1,77 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Select;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Select
+ */
+public class SelectTag extends AbstractRequiredListTag {
+
+ private static final long serialVersionUID = 6121715260335609618L;
+
+ protected String emptyOption;
+ protected String headerKey;
+ protected String headerValue;
+ protected String multiple;
+ protected String size;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Select(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ Select select = ((Select) component);
+ select.setEmptyOption(emptyOption);
+ select.setHeaderKey(headerKey);
+ select.setHeaderValue(headerValue);
+ select.setMultiple(multiple);
+ select.setSize(size);
+ }
+
+ public void setEmptyOption(String emptyOption) {
+ this.emptyOption = emptyOption;
+ }
+
+ public void setHeaderKey(String headerKey) {
+ this.headerKey = headerKey;
+ }
+
+ public void setHeaderValue(String headerValue) {
+ this.headerValue = headerValue;
+ }
+
+ public void setMultiple(String multiple) {
+ this.multiple = multiple;
+ }
+
+ public void setSize(String size) {
+ this.size = size;
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/SubmitTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/SubmitTag.java
new file mode 100644
index 000000000..da73a87b6
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/SubmitTag.java
@@ -0,0 +1,113 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Submit;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Submit
+ */
+public class SubmitTag extends AbstractUITag {
+
+ private static final long serialVersionUID = 2179281109958301343L;
+
+ protected String action;
+ protected String method;
+ protected String align;
+ protected String resultDivId;
+ protected String onLoadJS;
+ protected String notifyTopics;
+ protected String listenTopics;
+ protected String preInvokeJS;
+ protected String type;
+ protected String src;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Submit(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ Submit submit = ((Submit) component);
+ submit.setAction(action);
+ submit.setMethod(method);
+ submit.setAlign(align);
+ submit.setResultDivId(resultDivId);
+ submit.setOnLoadJS(onLoadJS);
+ submit.setNotifyTopics(notifyTopics);
+ submit.setListenTopics(listenTopics);
+ submit.setPreInvokeJS(preInvokeJS);
+ submit.setType(type);
+ submit.setSrc(src);
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public void setAlign(String align) {
+ this.align = align;
+ }
+
+ public void setResultDivId(String resultDivId) {
+ this.resultDivId = resultDivId;
+ }
+
+ public void setOnLoadJS(String onLoadJS) {
+ this.onLoadJS = onLoadJS;
+ }
+
+ public void setNotifyTopics(String notifyTopics) {
+ this.notifyTopics = notifyTopics;
+ }
+
+ public void setListenTopics(String listenTopics) {
+ this.listenTopics = listenTopics;
+ }
+
+ public void setPreInvokeJS(String preInvokeJS) {
+ this.preInvokeJS = preInvokeJS;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getSrc() {
+ return src;
+ }
+
+ public void setSrc(String src) {
+ this.src = src;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TabbedPanelTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TabbedPanelTag.java
new file mode 100644
index 000000000..eb8e65d2a
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TabbedPanelTag.java
@@ -0,0 +1,50 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Panel;
+import org.apache.struts2.components.TabbedPanel;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see TabbedPanel
+ */
+public class TabbedPanelTag extends AbstractClosingTag {
+
+ private static final long serialVersionUID = -4719930205515386252L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new TabbedPanel(stack, req, res);
+ }
+
+ public List getTabs() {
+ return ((TabbedPanel) component).getTabs();
+ }
+
+ public void addTab(Panel pane) {
+ ((TabbedPanel) component).addTab(pane);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TextFieldTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TextFieldTag.java
new file mode 100644
index 000000000..fcd69372f
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TextFieldTag.java
@@ -0,0 +1,70 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TextField;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see TextField
+ */
+public class TextFieldTag extends AbstractUITag {
+
+ private static final long serialVersionUID = 5811285953670562288L;
+
+ protected String maxlength;
+ protected String readonly;
+ protected String size;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new TextField(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ TextField textField = ((TextField) component);
+ textField.setMaxlength(maxlength);
+ textField.setReadonly(readonly);
+ textField.setSize(size);
+ }
+
+ /**
+ * @deprecated please use {@link #setMaxlength} instead
+ */
+ public void setMaxLength(String maxlength) {
+ this.maxlength = maxlength;
+ }
+
+ public void setMaxlength(String maxlength) {
+ this.maxlength = maxlength;
+ }
+
+ public void setReadonly(String readonly) {
+ this.readonly = readonly;
+ }
+
+ public void setSize(String size) {
+ this.size = size;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TextareaTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TextareaTag.java
new file mode 100644
index 000000000..7ad223913
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TextareaTag.java
@@ -0,0 +1,71 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TextArea;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see TextArea
+ */
+public class TextareaTag extends AbstractUITag {
+
+ private static final long serialVersionUID = -4107122506712927927L;
+
+ protected String cols;
+ protected String readonly;
+ protected String rows;
+ protected String wrap;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new TextArea(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ TextArea textArea = ((TextArea) component);
+ textArea.setCols(cols);
+ textArea.setReadonly(readonly);
+ textArea.setRows(rows);
+ textArea.setWrap(wrap);
+ }
+
+ public void setCols(String cols) {
+ this.cols = cols;
+ }
+
+ public void setReadonly(String readonly) {
+ this.readonly = readonly;
+ }
+
+ public void setRows(String rows) {
+ this.rows = rows;
+ }
+
+ public void setWrap(String wrap) {
+ this.wrap = wrap;
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TimePickerTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TimePickerTag.java
new file mode 100644
index 000000000..5d6e4af90
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TimePickerTag.java
@@ -0,0 +1,69 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TimePicker;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @version $Date$ $Id$
+ */
+public class TimePickerTag extends TextFieldTag {
+
+ private static final long serialVersionUID = 3527737048468381376L;
+
+ protected String format;
+ protected String timeIconPath;
+ protected String templatePath;
+ protected String templateCssPath;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new TimePicker(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ final TimePicker timePicker = (TimePicker) component;
+ timePicker.setFormat(format);
+ timePicker.setTimeIconPath(timeIconPath);
+ timePicker.setTemplatePath(templatePath);
+ timePicker.setTemplateCssPath(templateCssPath);
+ }
+
+ public void setFormat(String format) {
+ this.format = format;
+ }
+
+ public void setTimeIconPath(String timeIconPath) {
+ this.timeIconPath = timeIconPath;
+ }
+
+ public void setTemplatePath(String templatePath) {
+ this.templatePath = templatePath;
+ }
+
+ public void setTemplateCssPath(String templateCssPath) {
+ this.templateCssPath = templateCssPath;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TokenTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TokenTag.java
new file mode 100644
index 000000000..e3fdd9851
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TokenTag.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Token;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see Token
+ */
+public class TokenTag extends AbstractUITag {
+
+ private static final long serialVersionUID = 722480798151703457L;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Token(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TreeNodeTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TreeNodeTag.java
new file mode 100644
index 000000000..4020dcb1d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TreeNodeTag.java
@@ -0,0 +1,53 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TreeNode;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see TreeNode
+ */
+public class TreeNodeTag extends AbstractClosingTag {
+
+ private static final long serialVersionUID = 7340746943017900803L;
+
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new TreeNode(stack,req,res);
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ // NOTE: not necessary, label property is inherited, will be populated
+ // by super-class
+ /*protected void populateParams() {
+ if (label != null) {
+ TreeNode treeNode = (TreeNode)component;
+ treeNode.setLabel(label);
+ }
+ super.populateParams();
+ }*/
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TreeTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TreeTag.java
new file mode 100644
index 000000000..be672bdc7
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/TreeTag.java
@@ -0,0 +1,302 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Tree;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Tree
+ */
+public class TreeTag extends AbstractClosingTag {
+
+ private static final long serialVersionUID = 2735218501058548013L;
+
+ private String toggle;
+ private String treeSelectedTopic;
+ private String treeExpandedTopic;
+ private String treeCollapsedTopic;
+ private String rootNode;
+ private String childCollectionProperty;
+ private String nodeTitleProperty;
+ private String nodeIdProperty;
+ private String showRootGrid;
+
+ private String showGrid;
+ private String blankIconSrc;
+ private String gridIconSrcL;
+ private String gridIconSrcV;
+ private String gridIconSrcP;
+ private String gridIconSrcC;
+ private String gridIconSrcX;
+ private String gridIconSrcY;
+ private String expandIconSrcPlus;
+ private String expandIconSrcMinus;
+ private String iconWidth;
+ private String iconHeight;
+ private String toggleDuration;
+ private String templateCssPath;
+
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Tree(stack,req,res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ Tree tree = (Tree) component;
+ if (childCollectionProperty != null)
+ tree.setChildCollectionProperty(childCollectionProperty);
+ if (nodeIdProperty != null)
+ tree.setNodeIdProperty(nodeIdProperty);
+ if (nodeTitleProperty != null)
+ tree.setNodeTitleProperty(nodeTitleProperty);
+ if (rootNode != null)
+ tree.setRootNode(rootNode);
+ if (toggle != null)
+ tree.setToggle(toggle);
+ if (treeCollapsedTopic != null)
+ tree.setTreeCollapsedTopic(treeCollapsedTopic);
+ if (treeExpandedTopic != null)
+ tree.setTreeExpandedTopic(treeExpandedTopic);
+ if (treeSelectedTopic != null)
+ tree.setTreeSelectedTopic(treeSelectedTopic);
+ if (showRootGrid != null)
+ tree.setShowRootGrid(showRootGrid);
+
+ if (showGrid != null)
+ tree.setShowGrid(showGrid);
+ if (blankIconSrc != null)
+ tree.setBlankIconSrc(blankIconSrc);
+ if (gridIconSrcL != null)
+ tree.setGridIconSrcL(gridIconSrcC);
+ if (gridIconSrcV != null)
+ tree.setGridIconSrcV(gridIconSrcV);
+ if (gridIconSrcP != null)
+ tree.setGridIconSrcP(gridIconSrcP);
+ if (gridIconSrcC != null)
+ tree.setGridIconSrcC(gridIconSrcC);
+ if (gridIconSrcX != null)
+ tree.setGridIconSrcX(gridIconSrcX);
+ if (gridIconSrcY != null)
+ tree.setGridIconSrcY(gridIconSrcY);
+ if (expandIconSrcPlus != null)
+ tree.setExpandIconSrcPlus(expandIconSrcPlus);
+ if (expandIconSrcMinus != null)
+ tree.setExpandIconSrcMinus(expandIconSrcMinus);
+ if (iconWidth != null)
+ tree.setIconWidth(iconWidth);
+ if (iconHeight != null)
+ tree.setIconHeight(iconHeight);
+ if (toggleDuration != null)
+ tree.setToggleDuration(toggleDuration);
+ if (templateCssPath != null)
+ tree.setTemplateCssPath(templateCssPath);
+ }
+
+ public String getToggle() {
+ return toggle;
+ }
+
+ public void setToggle(String toggle) {
+ this.toggle = toggle;
+ }
+
+ public String getTreeSelectedTopic() {
+ return treeSelectedTopic;
+ }
+
+ public void setTreeSelectedTopic(String treeSelectedTopic) {
+ this.treeSelectedTopic = treeSelectedTopic;
+ }
+
+ public String getTreeExpandedTopic() {
+ return treeExpandedTopic;
+ }
+
+ public void setTreeExpandedTopic(String treeExpandedTopic) {
+ this.treeExpandedTopic = treeExpandedTopic;
+ }
+
+ public String getTreeCollapsedTopic() {
+ return treeCollapsedTopic;
+ }
+
+ public void setTreeCollapsedTopic(String treeCollapsedTopic) {
+ this.treeCollapsedTopic = treeCollapsedTopic;
+ }
+
+ public String getRootNode() {
+ return rootNode;
+ }
+
+ public void setRootNode(String rootNode) {
+ this.rootNode = rootNode;
+ }
+
+ public String getChildCollectionProperty() {
+ return childCollectionProperty;
+ }
+
+ public void setChildCollectionProperty(String childCollectionProperty) {
+ this.childCollectionProperty = childCollectionProperty;
+ }
+
+ public String getNodeTitleProperty() {
+ return nodeTitleProperty;
+ }
+
+ public void setNodeTitleProperty(String nodeTitleProperty) {
+ this.nodeTitleProperty = nodeTitleProperty;
+ }
+
+ public String getNodeIdProperty() {
+ return nodeIdProperty;
+ }
+
+ public void setNodeIdProperty(String nodeIdProperty) {
+ this.nodeIdProperty = nodeIdProperty;
+ }
+
+ public String getShowRootGrid() {
+ return showRootGrid;
+ }
+
+ public void setShowRootGrid(String showRootGrid) {
+ this.showRootGrid = showRootGrid;
+ }
+
+ public String getBlankIconSrc() {
+ return blankIconSrc;
+ }
+
+ public void setBlankIconSrc(String blankIconSrc) {
+ this.blankIconSrc = blankIconSrc;
+ }
+
+ public String getExpandIconSrcMinus() {
+ return expandIconSrcMinus;
+ }
+
+ public void setExpandIconSrcMinus(String expandIconSrcMinus) {
+ this.expandIconSrcMinus = expandIconSrcMinus;
+ }
+
+ public String getExpandIconSrcPlus() {
+ return expandIconSrcPlus;
+ }
+
+ public void setExpandIconSrcPlus(String expandIconSrcPlus) {
+ this.expandIconSrcPlus = expandIconSrcPlus;
+ }
+
+ public String getGridIconSrcC() {
+ return gridIconSrcC;
+ }
+
+ public void setGridIconSrcC(String gridIconSrcC) {
+ this.gridIconSrcC = gridIconSrcC;
+ }
+
+ public String getGridIconSrcL() {
+ return gridIconSrcL;
+ }
+
+ public void setGridIconSrcL(String gridIconSrcL) {
+ this.gridIconSrcL = gridIconSrcL;
+ }
+
+ public String getGridIconSrcP() {
+ return gridIconSrcP;
+ }
+
+ public void setGridIconSrcP(String gridIconSrcP) {
+ this.gridIconSrcP = gridIconSrcP;
+ }
+
+ public String getGridIconSrcV() {
+ return gridIconSrcV;
+ }
+
+ public void setGridIconSrcV(String gridIconSrcV) {
+ this.gridIconSrcV = gridIconSrcV;
+ }
+
+ public String getGridIconSrcX() {
+ return gridIconSrcX;
+ }
+
+ public void setGridIconSrcX(String gridIconSrcX) {
+ this.gridIconSrcX = gridIconSrcX;
+ }
+
+ public String getGridIconSrcY() {
+ return gridIconSrcY;
+ }
+
+ public void setGridIconSrcY(String gridIconSrcY) {
+ this.gridIconSrcY = gridIconSrcY;
+ }
+
+ public String getIconHeight() {
+ return iconHeight;
+ }
+
+ public void setIconHeight(String iconHeight) {
+ this.iconHeight = iconHeight;
+ }
+
+ public String getIconWidth() {
+ return iconWidth;
+ }
+
+ public void setIconWidth(String iconWidth) {
+ this.iconWidth = iconWidth;
+ }
+
+ public String getTemplateCssPath() {
+ return templateCssPath;
+ }
+
+ public void setTemplateCssPath(String templateCssPath) {
+ this.templateCssPath = templateCssPath;
+ }
+
+ public String getToggleDuration() {
+ return toggleDuration;
+ }
+
+ public void setToggleDuration(String toggleDuration) {
+ this.toggleDuration = toggleDuration;
+ }
+
+ public String getShowGrid() {
+ return showGrid;
+ }
+
+ public void setShowGrid(String showGrid) {
+ this.showGrid = showGrid;
+ }
+}
+
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/UpDownSelectTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/UpDownSelectTag.java
new file mode 100644
index 000000000..3879901ff
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/UpDownSelectTag.java
@@ -0,0 +1,120 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.UpDownSelect;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see UpDownSelect
+ */
+public class UpDownSelectTag extends SelectTag {
+
+ private static final long serialVersionUID = -8136573053799541353L;
+
+ protected String allowMoveUp;
+ protected String allowMoveDown;
+ protected String allowSelectAll;
+
+ protected String moveUpLabel;
+ protected String moveDownLabel;
+ protected String selectAllLabel;
+
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new UpDownSelect(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ UpDownSelect c = (UpDownSelect) component;
+
+ c.setAllowMoveUp(allowMoveUp);
+ c.setAllowMoveDown(allowMoveDown);
+ c.setAllowSelectAll(allowSelectAll);
+
+ c.setMoveUpLabel(moveUpLabel);
+ c.setMoveDownLabel(moveDownLabel);
+ c.setSelectAllLabel(selectAllLabel);
+
+ }
+
+
+ public String getAllowMoveUp() {
+ return allowMoveUp;
+ }
+
+ public void setAllowMoveUp(String allowMoveUp) {
+ this.allowMoveUp = allowMoveUp;
+ }
+
+
+
+ public String getAllowMoveDown() {
+ return allowMoveDown;
+ }
+
+ public void setAllowMoveDown(String allowMoveDown) {
+ this.allowMoveDown = allowMoveDown;
+ }
+
+
+
+ public String getAllowSelectAll() {
+ return allowSelectAll;
+ }
+
+ public void setAllowSelectAll(String allowSelectAll) {
+ this.allowSelectAll = allowSelectAll;
+ }
+
+
+ public String getMoveUpLabel() {
+ return moveUpLabel;
+ }
+
+ public void setMoveUpLabel(String moveUpLabel) {
+ this.moveUpLabel = moveUpLabel;
+ }
+
+
+
+ public String getMoveDownLabel() {
+ return moveDownLabel;
+ }
+
+ public void setMoveDownLabel(String moveDownLabel) {
+ this.moveDownLabel = moveDownLabel;
+ }
+
+
+
+ public String getSelectAllLabel() {
+ return selectAllLabel;
+ }
+
+ public void setSelectAllLabel(String selectAllLabel) {
+ this.selectAllLabel = selectAllLabel;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/table/WebTableTag.java b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/table/WebTableTag.java
new file mode 100644
index 000000000..6234723f1
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/jsp/ui/table/WebTableTag.java
@@ -0,0 +1,71 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.jsp.ui.table;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.table.WebTable;
+import org.apache.struts2.views.jsp.ui.ComponentTag;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * @see WebTable
+ */
+public class WebTableTag extends ComponentTag {
+
+ private static final long serialVersionUID = 2978932111492397942L;
+
+ protected String sortOrder;
+ protected String modelName;
+ protected boolean sortable;
+ protected int sortColumn;
+
+ public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new WebTable(stack, req, res);
+ }
+
+ protected void populateParams() {
+ super.populateParams();
+
+ WebTable table = (WebTable) component;
+ table.setSortOrder(sortOrder);
+ table.setSortable(sortable);
+ table.setModelName(modelName);
+ table.setSortOrder(sortOrder);
+ }
+
+ public void setSortOrder(String sortOrder) {
+ this.sortOrder = sortOrder;
+ }
+
+ public void setModelName(String modelName) {
+ this.modelName = modelName;
+ }
+
+ public void setSortable(boolean sortable) {
+ this.sortable = sortable;
+ }
+
+ public void setSortColumn(int sortColumn) {
+ this.sortColumn = sortColumn;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/util/ContextUtil.java b/trunk/core/src/main/java/org/apache/struts2/views/util/ContextUtil.java
new file mode 100644
index 000000000..e3cf24072
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/util/ContextUtil.java
@@ -0,0 +1,94 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.config.Settings;
+import org.apache.struts2.util.StrutsUtil;
+import org.apache.struts2.views.jsp.ui.OgnlTool;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * Value Stack's Context related Utilities.
+ *
+ */
+public class ContextUtil {
+ public static final String REQUEST = "request";
+ public static final String REQUEST2 = "request";
+ public static final String RESPONSE = "response";
+ public static final String RESPONSE2 = "response";
+ public static final String SESSION = "session";
+ public static final String BASE = "base";
+ public static final String STACK = "stack";
+ public static final String OGNL = "ognl";
+ public static final String STRUTS = "struts";
+ public static final String ACTION = "action";
+
+ public static Map getStandardContext(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ HashMap map = new HashMap();
+ map.put(REQUEST, req);
+ map.put(REQUEST2, req);
+ map.put(RESPONSE, res);
+ map.put(RESPONSE2, res);
+ map.put(SESSION, req.getSession(false));
+ map.put(BASE, req.getContextPath());
+ map.put(STACK, stack);
+ map.put(OGNL, OgnlTool.getInstance());
+ map.put(STRUTS, new StrutsUtil(stack, req, res));
+
+ ActionInvocation invocation = (ActionInvocation) stack.getContext().get(ActionContext.ACTION_INVOCATION);
+ if (invocation != null) {
+ map.put(ACTION, invocation.getAction());
+ }
+ return map;
+ }
+
+ /**
+ * Return true if either Configuration's altSyntax is on or the stack context's useAltSyntax is on
+ * @param context stack's context
+ * @return boolean
+ */
+ public static boolean isUseAltSyntax(Map context) {
+ // We didn't make altSyntax static cause, if so, struts.configuration.xml.reload will not work
+ // plus the Configuration implementation should cache the properties, which the framework's
+ // configuration implementation does
+ boolean altSyntax = "true".equals(Settings.get(StrutsConstants.STRUTS_TAG_ALTSYNTAX));
+ return altSyntax ||(
+ (context.containsKey("useAltSyntax") &&
+ context.get("useAltSyntax") != null &&
+ "true".equals(context.get("useAltSyntax").toString())));
+ }
+
+ /**
+ * Returns a String for overriding the default templateSuffix if templateSuffix is on the stack
+ * @param context stack's context
+ * @return String
+ */
+ public static String getTemplateSuffix(Map context) {
+ return context.containsKey("templateSuffix") ? (String) context.get("templateSuffix") : null;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/util/ResourceUtil.java b/trunk/core/src/main/java/org/apache/struts2/views/util/ResourceUtil.java
new file mode 100644
index 000000000..a0f8fd1bc
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/util/ResourceUtil.java
@@ -0,0 +1,35 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.util;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.struts2.RequestUtils;
+
+/**
+ */
+public class ResourceUtil {
+ public static String getResourceBase(HttpServletRequest req) {
+ String path = RequestUtils.getServletPath(req);
+ if (path == null || "".equals(path)) {
+ return "";
+ }
+
+ return path.substring(0, path.lastIndexOf('/'));
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/util/TextUtil.java b/trunk/core/src/main/java/org/apache/struts2/views/util/TextUtil.java
new file mode 100644
index 000000000..21d5c99a1
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/util/TextUtil.java
@@ -0,0 +1,242 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.util;
+
+
+/**
+ * This class handles HTML escaping of text.
+ * It was written and optimized to be as fast as possible.
+ *
+ */
+public class TextUtil {
+
+ protected static final int MAX_LENGTH = 300;
+
+ /**
+ * We use arrays of char in the lookup table because it is faster
+ * appending this to a StringBuffer than appending a String
+ */
+ protected static final char[][] _stringChars = new char[MAX_LENGTH][];
+
+ static {
+ // Initialize the mapping table
+ initMapping();
+ }
+
+
+ /**
+ * Call escapeHTML(s, false)
+ */
+ public static final String escapeHTML(String s) {
+ return escapeHTML(s, false);
+ }
+
+ /**
+ * Escape HTML.
+ *
+ * @param s string to be escaped
+ * @param escapeEmpty if true, then empty string will be escaped.
+ */
+ public static final String escapeHTML(String s, boolean escapeEmpty) {
+ int len = s.length();
+
+ if (len == 0) {
+ return s;
+ }
+
+ if (!escapeEmpty) {
+ String trimmed = s.trim();
+
+ if ((trimmed.length() == 0) || ("\"\"").equals(trimmed)) {
+ return s;
+ }
+ }
+
+ int i = 0;
+
+ // First loop through String and check if escaping is needed at all
+ // No buffers are copied at this time
+ do {
+ int index = s.charAt(i);
+
+ if (index >= MAX_LENGTH) {
+ if (index != 0x20AC) { // If not euro symbol
+
+ continue;
+ }
+
+ break;
+ } else if (_stringChars[index] != null) {
+ break;
+ }
+ } while (++i < len);
+
+ // If the check went to the end with no escaping then i should be == len now
+ // otherwise we must continue escaping for real
+ if (i == len) {
+ return s;
+ }
+
+ // We found a character to escape and broke out at position i
+ // Now copy all characters before that to StringBuffer sb
+ // Since a char[] will be used for copying we might as well get
+ // a complete copy of it so that we can use array indexing instead of charAt
+ StringBuffer sb = new StringBuffer(len + 40);
+ char[] chars = new char[len];
+
+ // Copy all chars from the String s to the chars buffer
+ s.getChars(0, len, chars, 0);
+
+ // Append the first i characters that we have checked to the resulting StringBuffer
+ sb.append(chars, 0, i);
+
+ int last = i;
+ char[] subst;
+
+ for (; i < len; i++) {
+ char c = chars[i];
+ int index = c;
+
+ if (index < MAX_LENGTH) {
+ subst = _stringChars[index];
+
+ // It is faster to append a char[] than a String which is why we use this
+ if (subst != null) {
+ if (i > last) {
+ sb.append(chars, last, i - last);
+ }
+
+ sb.append(subst);
+ last = i + 1;
+ }
+ }
+ // Check if it is the euro symbol. This could be changed to check in a second lookup
+ // table in case one wants to convert more characters in that area
+ else if (index == 0x20AC) {
+ if (i > last) {
+ sb.append(chars, last, i - last);
+ }
+
+ sb.append("€");
+ last = i + 1;
+ }
+ }
+
+ if (i > last) {
+ sb.append(chars, last, i - last);
+ }
+
+ return sb.toString();
+ }
+
+ protected static void addMapping(int c, String txt, String[] strings) {
+ strings[c] = txt;
+ }
+
+ protected static void initMapping() {
+ String[] strings = new String[MAX_LENGTH];
+
+ addMapping(0x22, """, strings); // "
+ addMapping(0x26, "&", strings); // &
+ addMapping(0x3c, "<", strings); // <
+ addMapping(0x3e, ">", strings); // >
+
+ addMapping(0xa1, "¡", strings); //
+ addMapping(0xa2, "¢", strings); //
+ addMapping(0xa3, "£", strings); //
+ addMapping(0xa9, "©", strings); // �
+ addMapping(0xae, "®", strings); // �
+ addMapping(0xbf, "¿", strings); //
+
+ addMapping(0xc0, "À", strings); // �
+ addMapping(0xc1, "Á", strings); // �
+ addMapping(0xc2, "Â", strings); // �
+ addMapping(0xc3, "Ã", strings); // �
+ addMapping(0xc4, "Ä", strings); // �
+ addMapping(0xc5, "Å", strings); // �
+ addMapping(0xc6, "Æ", strings); // �
+ addMapping(0xc7, "Ç", strings); // �
+ addMapping(0xc8, "È", strings); //
+ addMapping(0xc9, "É", strings); //
+ addMapping(0xca, "Ê", strings); //
+ addMapping(0xcb, "Ë", strings); //
+ addMapping(0xcc, "Ì", strings); //
+ addMapping(0xcd, "Í", strings); //
+ addMapping(0xce, "Î", strings); //
+ addMapping(0xcf, "Ï", strings); //
+
+ addMapping(0xd0, "Ð", strings); //
+ addMapping(0xd1, "Ñ", strings); //
+ addMapping(0xd2, "Ò", strings); //
+ addMapping(0xd3, "Ó", strings); //
+ addMapping(0xd4, "Ô", strings); //
+ addMapping(0xd5, "Õ", strings); //
+ addMapping(0xd6, "Ö", strings); // �
+ addMapping(0xd7, "×", strings); //
+ addMapping(0xd8, "Ø", strings); //
+ addMapping(0xd9, "Ù", strings); //
+ addMapping(0xda, "Ú", strings); //
+ addMapping(0xdb, "Û", strings); //
+ addMapping(0xdc, "Ü", strings); //
+ addMapping(0xdd, "Ý", strings); //
+ addMapping(0xde, "Þ", strings); //
+ addMapping(0xdf, "ß", strings); //
+
+ addMapping(0xe0, "à", strings); //
+ addMapping(0xe1, "á", strings); //
+ addMapping(0xe2, "â", strings); //
+ addMapping(0xe3, "ã", strings); //
+ addMapping(0xe4, "ä", strings); // �
+ addMapping(0xe5, "å", strings); // �
+ addMapping(0xe6, "æ", strings); //
+ addMapping(0xe7, "ç", strings); //
+ addMapping(0xe8, "è", strings); //
+ addMapping(0xe9, "é", strings); //
+ addMapping(0xea, "ê", strings); //
+ addMapping(0xeb, "ë", strings); //
+ addMapping(0xec, "ì", strings); //
+ addMapping(0xed, "í", strings); //
+ addMapping(0xee, "î", strings); //
+ addMapping(0xef, "ï", strings); //
+
+ addMapping(0xf0, "ð", strings); //
+ addMapping(0xf1, "ñ", strings); //
+ addMapping(0xf2, "ò", strings); //
+ addMapping(0xf3, "ó", strings); //
+ addMapping(0xf4, "ô", strings); //
+ addMapping(0xf5, "õ", strings); //
+ addMapping(0xf6, "ö", strings); // �
+ addMapping(0xf7, "÷", strings); //
+ addMapping(0xf8, "ø", strings); //
+ addMapping(0xf9, "ù", strings); //
+ addMapping(0xfa, "ú", strings); //
+ addMapping(0xfb, "û", strings); //
+ addMapping(0xfc, "ü", strings); //
+ addMapping(0xfd, "ý", strings); //
+ addMapping(0xfe, "þ", strings); //
+ addMapping(0xff, "ÿ", strings); //
+
+ for (int i = 0; i < strings.length; i++) {
+ String str = strings[i];
+
+ if (str != null) {
+ _stringChars[i] = str.toCharArray();
+ }
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java b/trunk/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java
new file mode 100644
index 000000000..75f81cf79
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java
@@ -0,0 +1,307 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.util;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.config.Settings;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.util.TextParseUtil;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.XWorkContinuationConfig;
+
+
+/**
+ * UrlHelper
+ *
+ */
+public class UrlHelper {
+ private static final Log LOG = LogFactory.getLog(UrlHelper.class);
+
+ /**
+ * Default HTTP port (80).
+ */
+ private static final int DEFAULT_HTTP_PORT = 80;
+
+ /**
+ * Default HTTPS port (443).
+ */
+ private static final int DEFAULT_HTTPS_PORT = 443;
+
+ private static final String AMP = "&";
+
+ public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params) {
+ return buildUrl(action, request, response, params, null, true, true);
+ }
+
+ public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, boolean includeContext, boolean encodeResult) {
+ return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, false);
+ }
+
+ public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort) {
+ StringBuffer link = new StringBuffer();
+
+ boolean changedScheme = false;
+
+ int httpPort = DEFAULT_HTTP_PORT;
+
+ try {
+ httpPort = Integer.parseInt((String) Settings.get(StrutsConstants.STRUTS_URL_HTTP_PORT));
+ } catch (Exception ex) {
+ }
+
+ int httpsPort = DEFAULT_HTTPS_PORT;
+
+ try {
+ httpsPort = Integer.parseInt((String) Settings.get(StrutsConstants.STRUTS_URL_HTTPS_PORT));
+ } catch (Exception ex) {
+ }
+
+ // only append scheme if it is different to the current scheme *OR*
+ // if we explicity want it to be appended by having forceAddSchemeHostAndPort = true
+ if (forceAddSchemeHostAndPort) {
+ String reqScheme = request.getScheme();
+ changedScheme = true;
+ link.append(scheme != null ? scheme : reqScheme);
+ link.append("://");
+ link.append(request.getServerName());
+
+ if ((scheme.equals("http") && (httpPort != DEFAULT_HTTP_PORT)) || (scheme.equals("https") && httpsPort != DEFAULT_HTTPS_PORT))
+ {
+ link.append(":");
+ link.append(scheme.equals("http") ? httpPort : httpsPort);
+ }
+ }
+ else if (
+ (scheme != null) && !scheme.equals(request.getScheme())) {
+ changedScheme = true;
+ link.append(scheme);
+ link.append("://");
+ link.append(request.getServerName());
+
+ if ((scheme.equals("http") && (httpPort != DEFAULT_HTTP_PORT)) || (scheme.equals("https") && httpsPort != DEFAULT_HTTPS_PORT))
+ {
+ link.append(":");
+ link.append(scheme.equals("http") ? httpPort : httpsPort);
+ }
+ }
+
+ if (action != null) {
+ // Check if context path needs to be added
+ // Add path to absolute links
+ if (action.startsWith("/") && includeContext) {
+ String contextPath = request.getContextPath();
+ if (!contextPath.equals("/")) {
+ link.append(contextPath);
+ }
+ } else if (changedScheme) {
+
+ // (Applicable to Servlet 2.4 containers)
+ // If the request was forwarded, the attribute below will be set with the original URL
+ String uri = (String) request.getAttribute("javax.servlet.forward.request_uri");
+
+ // If the attribute wasn't found, default to the value in the request object
+ if (uri == null) {
+ uri = request.getRequestURI();
+ }
+
+ link.append(uri.substring(0, uri.lastIndexOf('/') + 1));
+ }
+
+ // Add page
+ link.append(action);
+ } else {
+ // Go to "same page"
+ String requestURI = (String) request.getAttribute("struts.request_uri");
+
+ // (Applicable to Servlet 2.4 containers)
+ // If the request was forwarded, the attribute below will be set with the original URL
+ if (requestURI == null) {
+ requestURI = (String) request.getAttribute("javax.servlet.forward.request_uri");
+ }
+
+ // If neither request attributes were found, default to the value in the request object
+ if (requestURI == null) {
+ requestURI = request.getRequestURI();
+ }
+
+ link.append(requestURI);
+ }
+
+ // tie in the continuation parameter
+ String continueId = (String) ActionContext.getContext().get(XWorkContinuationConfig.CONTINUE_KEY);
+ if (continueId != null) {
+ if (params == null) {
+ params = Collections.singletonMap(XWorkContinuationConfig.CONTINUE_PARAM, continueId);
+ } else {
+ params.put(XWorkContinuationConfig.CONTINUE_PARAM, continueId);
+ }
+ }
+
+ //if the action was not explicitly set grab the params from the request
+ buildParametersString(params, link);
+
+ String result;
+
+ try {
+ result = encodeResult ? response.encodeURL(link.toString()) : link.toString();
+ } catch (Exception ex) {
+ // Could not encode the URL for some reason
+ // Use it unchanged
+ result = link.toString();
+ }
+
+ return result;
+ }
+
+ public static void buildParametersString(Map params, StringBuffer link) {
+ buildParametersString(params, link, AMP);
+ }
+
+ public static void buildParametersString(Map params, StringBuffer link, String paramSeparator) {
+ if ((params != null) && (params.size() > 0)) {
+ if (link.toString().indexOf("?") == -1) {
+ link.append("?");
+ } else {
+ link.append(paramSeparator);
+ }
+
+ // Set params
+ Iterator iter = params.entrySet().iterator();
+
+ String[] valueHolder = new String[1];
+
+ while (iter.hasNext()) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String name = (String) entry.getKey();
+ Object value = entry.getValue();
+
+ String[] values;
+
+ if (value instanceof String[]) {
+ values = (String[]) value;
+ } else {
+ valueHolder[0] = value.toString();
+ values = valueHolder;
+ }
+
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] != null) {
+ link.append(name);
+ link.append('=');
+ link.append(translateAndEncode(values[i]));
+ }
+
+ if (i < (values.length - 1)) {
+ link.append(paramSeparator);
+ }
+ }
+
+ if (iter.hasNext()) {
+ link.append(paramSeparator);
+ }
+ }
+ }
+ }
+
+ /**
+ * Translates any script expressions using {@link com.opensymphony.xwork2.util.TextParseUtil#translateVariables} and
+ * encodes the URL using {@link java.net.URLEncoder#encode} with the encoding specified in the configuration.
+ *
+ * @param input
+ * @return the translated and encoded string
+ */
+ public static String translateAndEncode(String input) {
+ String translatedInput = translateVariable(input);
+ String encoding = getEncodingFromConfiguration();
+
+ try {
+ return URLEncoder.encode(translatedInput, encoding);
+ } catch (UnsupportedEncodingException e) {
+ LOG.warn("Could not encode URL parameter '" + input + "', returning value un-encoded");
+ return translatedInput;
+ }
+ }
+
+ public static String translateAndDecode(String input) {
+ String translatedInput = translateVariable(input);
+ String encoding = getEncodingFromConfiguration();
+
+ try {
+ return URLDecoder.decode(translatedInput, encoding);
+ } catch (UnsupportedEncodingException e) {
+ LOG.warn("Could not encode URL parameter '" + input + "', returning value un-encoded");
+ return translatedInput;
+ }
+ }
+
+ private static String translateVariable(String input) {
+ ValueStack valueStack = ServletActionContext.getContext().getValueStack();
+ String output = TextParseUtil.translateVariables(input, valueStack);
+ return output;
+ }
+
+ private static String getEncodingFromConfiguration() {
+ final String encoding;
+ if (Settings.isSet(StrutsConstants.STRUTS_I18N_ENCODING)) {
+ encoding = Settings.get(StrutsConstants.STRUTS_I18N_ENCODING);
+ } else {
+ encoding = "UTF-8";
+ }
+ return encoding;
+ }
+
+ public static Map parseQueryString(String queryString) {
+ Map queryParams = new LinkedHashMap();
+ if (queryString != null) {
+ String[] params = queryString.split("&");
+ for (int a=0; a< params.length; a++) {
+ if (params[a].trim().length() > 0) {
+ String[] tmpParams = params[a].split("=");
+ String paramName = null;
+ String paramValue = "";
+ if (tmpParams.length > 0) {
+ paramName = tmpParams[0];
+ }
+ if (tmpParams.length > 1) {
+ paramValue = tmpParams[1];
+ }
+ if (paramName != null) {
+ String translatedParamValue = translateAndDecode(paramValue);
+ queryParams.put(paramName, translatedParamValue);
+ }
+ }
+ }
+ }
+ return queryParams;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/util/package.html b/trunk/core/src/main/java/org/apache/struts2/views/util/package.html
new file mode 100644
index 000000000..27945fae8
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/util/package.html
@@ -0,0 +1 @@
+Miscellaneous helper classes for all views.
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/StrutsResourceLoader.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/StrutsResourceLoader.java
new file mode 100644
index 000000000..59c909927
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/StrutsResourceLoader.java
@@ -0,0 +1,48 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity;
+
+import java.io.InputStream;
+
+import org.apache.struts2.util.ClassLoaderUtils;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
+
+
+/**
+ * Loads resource from the Thread's context ClassLoader.
+ *
+ */
+public class StrutsResourceLoader extends ClasspathResourceLoader {
+
+ public synchronized InputStream getResourceStream(String name) throws ResourceNotFoundException {
+ if ((name == null) || (name.length() == 0)) {
+ throw new ResourceNotFoundException("No template name provided");
+ }
+
+ if (name.startsWith("/")) {
+ name = name.substring(1);
+ }
+
+ try {
+ return ClassLoaderUtils.getResourceAsStream(name, StrutsResourceLoader.class);
+ } catch (Exception e) {
+ throw new ResourceNotFoundException(e.getMessage());
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java
new file mode 100644
index 000000000..e163c5337
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java
@@ -0,0 +1,112 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity;
+
+import org.apache.velocity.VelocityContext;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ */
+public class StrutsVelocityContext extends VelocityContext {
+
+ private static final long serialVersionUID = 8497212428904436963L;
+ ValueStack stack;
+ VelocityContext[] chainedContexts;
+
+
+ public StrutsVelocityContext(ValueStack stack) {
+ this(null, stack);
+ }
+
+ public StrutsVelocityContext(VelocityContext[] chainedContexts, ValueStack stack) {
+ this.chainedContexts = chainedContexts;
+ this.stack = stack;
+ }
+
+
+ public boolean internalContainsKey(Object key) {
+ boolean contains = super.internalContainsKey(key);
+
+ // first let's check to see if we contain the requested key
+ if (contains) {
+ return true;
+ }
+
+ // if not, let's search for the key in the ognl value stack
+ if (stack != null) {
+ Object o = stack.findValue(key.toString());
+
+ if (o != null) {
+ return true;
+ }
+
+ o = stack.getContext().get(key.toString());
+ if (o != null) {
+ return true;
+ }
+ }
+
+ // if we still haven't found it, le's search through our chained contexts
+ if (chainedContexts != null) {
+ for (int index = 0; index < chainedContexts.length; index++) {
+ if (chainedContexts[index].containsKey(key)) {
+ return true;
+ }
+ }
+ }
+
+ // nope, i guess it's really not here
+ return false;
+ }
+
+ public Object internalGet(String key) {
+ // first, let's check to see if have the requested value
+ if (super.internalContainsKey(key)) {
+ return super.internalGet(key);
+ }
+
+ // still no luck? let's look against the value stack
+ if (stack != null) {
+ Object object = stack.findValue(key);
+
+ if (object != null) {
+ return object;
+ }
+
+ object = stack.getContext().get(key);
+ if (object != null) {
+ return object;
+ }
+
+ }
+
+ // finally, if we're chained to other contexts, let's look in them
+ if (chainedContexts != null) {
+ for (int index = 0; index < chainedContexts.length; index++) {
+ if (chainedContexts[index].containsKey(key)) {
+ return chainedContexts[index].internalGet(key);
+ }
+ }
+ }
+
+ // nope, i guess it's really not here
+ return null;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityServlet.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityServlet.java
new file mode 100644
index 000000000..7107561fe
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityServlet.java
@@ -0,0 +1,135 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.Properties;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspFactory;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.struts2.RequestUtils;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.config.Settings;
+import org.apache.struts2.views.util.ContextUtil;
+import org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.servlet.VelocityServlet;
+
+import com.opensymphony.xwork2.ActionContext;
+
+
+/**
+ * @deprecated please use {@link org.apache.struts2.dispatcher.VelocityResult} instead of direct access
+ */
+public class StrutsVelocityServlet extends VelocityServlet {
+ private static final long serialVersionUID = -2078492831396251182L;
+ private VelocityManager velocityManager;
+
+ public StrutsVelocityServlet() {
+ velocityManager = VelocityManager.getInstance();
+ }
+
+ public void init(ServletConfig servletConfig) throws ServletException {
+ super.init(servletConfig);
+
+ // initialize our VelocityManager
+ velocityManager.init(servletConfig.getServletContext());
+ }
+
+ protected Context createContext(HttpServletRequest request, HttpServletResponse response) {
+ return velocityManager.createContext(ActionContext.getContext().getValueStack(), request, response);
+ }
+
+ protected Template handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Context context) throws Exception {
+ String servletPath = (String) httpServletRequest.getAttribute("javax.servlet.include.servlet_path");
+
+ if (servletPath == null) {
+ servletPath = RequestUtils.getServletPath(httpServletRequest);
+ }
+
+ return getTemplate(servletPath, getEncoding());
+ }
+
+ /**
+ * This method extends the VelocityServlet's loadConfiguration method by performing the following actions:
+ *
+ * invokes VelocityServlet.loadConfiguration to create a properties object
+ * alters the RESOURCE_LOADER to include a class loader
+ * configures the class loader using the StrutsResourceLoader
+ *
+ *
+ * @param servletConfig
+ * @throws IOException
+ * @throws FileNotFoundException
+ * @see org.apache.velocity.servlet.VelocityServlet#loadConfiguration
+ */
+ protected Properties loadConfiguration(ServletConfig servletConfig) throws IOException, FileNotFoundException {
+ return velocityManager.loadConfiguration(servletConfig.getServletContext());
+ }
+
+ /**
+ * create a PageContext and render the template to PageContext.getOut()
+ *
+ * @see VelocityServlet#mergeTemplate(Template, Context, HttpServletResponse) for additional documentation
+ */
+ protected void mergeTemplate(Template template, Context context, HttpServletResponse response) throws ResourceNotFoundException, ParseErrorException, MethodInvocationException, IOException, UnsupportedEncodingException, Exception {
+ // save the old PageContext
+ PageContext oldPageContext = ServletActionContext.getPageContext();
+
+ // create a new PageContext
+ JspFactory jspFactory = JspFactory.getDefaultFactory();
+ HttpServletRequest request = (HttpServletRequest) context.get(ContextUtil.REQUEST);
+ PageContext pageContext = jspFactory.getPageContext(this, request, response, null, true, 8192, true);
+
+ // put the new PageContext into ActionContext
+ ActionContext actionContext = ActionContext.getContext();
+ actionContext.put(ServletActionContext.PAGE_CONTEXT, pageContext);
+
+ try {
+ Writer writer = pageContext.getOut();
+ template.merge(context, writer);
+ writer.flush();
+ } finally {
+ // perform cleanup
+ jspFactory.releasePageContext(pageContext);
+ actionContext.put(ServletActionContext.PAGE_CONTEXT, oldPageContext);
+ }
+ }
+
+ private String getEncoding() {
+ // todo look into converting this to using XWork/Struts encoding rules
+ try {
+ return Settings.get(StrutsConstants.STRUTS_I18N_ENCODING);
+ } catch (IllegalArgumentException e) {
+ return RuntimeSingleton.getString(RuntimeSingleton.OUTPUT_ENCODING, DEFAULT_OUTPUT_ENCODING);
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/VelocityManager.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/VelocityManager.java
new file mode 100644
index 000000000..62477a8b9
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/VelocityManager.java
@@ -0,0 +1,672 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.StrutsException;
+import org.apache.struts2.config.Settings;
+import org.apache.struts2.util.VelocityStrutsUtil;
+import org.apache.struts2.views.jsp.ui.OgnlTool;
+import org.apache.struts2.views.util.ContextUtil;
+import org.apache.struts2.views.velocity.components.ActionDirective;
+import org.apache.struts2.views.velocity.components.ActionErrorDirective;
+import org.apache.struts2.views.velocity.components.ActionMessageDirective;
+import org.apache.struts2.views.velocity.components.AnchorDirective;
+import org.apache.struts2.views.velocity.components.BeanDirective;
+import org.apache.struts2.views.velocity.components.CheckBoxDirective;
+import org.apache.struts2.views.velocity.components.CheckBoxListDirective;
+import org.apache.struts2.views.velocity.components.ComboBoxDirective;
+import org.apache.struts2.views.velocity.components.ComponentDirective;
+import org.apache.struts2.views.velocity.components.DateDirective;
+import org.apache.struts2.views.velocity.components.DatePickerDirective;
+import org.apache.struts2.views.velocity.components.DivDirective;
+import org.apache.struts2.views.velocity.components.DoubleSelectDirective;
+import org.apache.struts2.views.velocity.components.FieldErrorDirective;
+import org.apache.struts2.views.velocity.components.FileDirective;
+import org.apache.struts2.views.velocity.components.FormDirective;
+import org.apache.struts2.views.velocity.components.HeadDirective;
+import org.apache.struts2.views.velocity.components.HiddenDirective;
+import org.apache.struts2.views.velocity.components.I18nDirective;
+import org.apache.struts2.views.velocity.components.IncludeDirective;
+import org.apache.struts2.views.velocity.components.LabelDirective;
+import org.apache.struts2.views.velocity.components.OptionTransferSelectDirective;
+import org.apache.struts2.views.velocity.components.PanelDirective;
+import org.apache.struts2.views.velocity.components.ParamDirective;
+import org.apache.struts2.views.velocity.components.PasswordDirective;
+import org.apache.struts2.views.velocity.components.PropertyDirective;
+import org.apache.struts2.views.velocity.components.PushDirective;
+import org.apache.struts2.views.velocity.components.RadioDirective;
+import org.apache.struts2.views.velocity.components.ResetDirective;
+import org.apache.struts2.views.velocity.components.SelectDirective;
+import org.apache.struts2.views.velocity.components.SetDirective;
+import org.apache.struts2.views.velocity.components.SubmitDirective;
+import org.apache.struts2.views.velocity.components.TabbedPanelDirective;
+import org.apache.struts2.views.velocity.components.TextAreaDirective;
+import org.apache.struts2.views.velocity.components.TextDirective;
+import org.apache.struts2.views.velocity.components.TextFieldDirective;
+import org.apache.struts2.views.velocity.components.TokenDirective;
+import org.apache.struts2.views.velocity.components.TreeDirective;
+import org.apache.struts2.views.velocity.components.TreeNodeDirective;
+import org.apache.struts2.views.velocity.components.URLDirective;
+import org.apache.struts2.views.velocity.components.UpDownSelectDirective;
+import org.apache.struts2.views.velocity.components.WebTableDirective;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.tools.view.ToolboxManager;
+import org.apache.velocity.tools.view.context.ChainedContext;
+import org.apache.velocity.tools.view.servlet.ServletToolboxManager;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * Manages the environment for Velocity result types
+ *
+ */
+public class VelocityManager {
+ private static final Log log = LogFactory.getLog(VelocityManager.class);
+ private static VelocityManager instance;
+ public static final String STRUTS = "struts";
+
+ /**
+ * the parent JSP tag
+ */
+ public static final String PARENT = "parent";
+
+ /**
+ * the current JSP tag
+ */
+ public static final String TAG = "tag";
+
+ private VelocityEngine velocityEngine;
+
+ /**
+ * A reference to the toolbox manager.
+ */
+ protected ToolboxManager toolboxManager = null;
+ private String toolBoxLocation;
+
+
+ /**
+ * Names of contexts that will be chained on every request
+ */
+ private String[] chainedContextNames;
+
+ private Properties velocityProperties;
+
+ protected VelocityManager() {
+ init();
+ }
+
+ /**
+ * retrieve an instance to the current VelocityManager
+ */
+ public synchronized static VelocityManager getInstance() {
+ if (instance == null) {
+ String classname = VelocityManager.class.getName();
+
+ if (Settings.isSet(StrutsConstants.STRUTS_VELOCITY_MANAGER_CLASSNAME)) {
+ classname = Settings.get(StrutsConstants.STRUTS_VELOCITY_MANAGER_CLASSNAME).trim();
+ }
+
+ if (!classname.equals(VelocityManager.class.getName())) {
+ try {
+ log.info("Instantiating VelocityManager!, " + classname);
+ // singleton instances shouldn't be built accessing request or session-specific context data
+ instance = (VelocityManager) ObjectFactory.getObjectFactory().buildBean(classname, null);
+ } catch (Exception e) {
+ log.fatal("Fatal exception occurred while trying to instantiate a VelocityManager instance, " + classname, e);
+ instance = new VelocityManager();
+ }
+ } else {
+ instance = new VelocityManager();
+ }
+ }
+
+ return instance;
+ }
+
+ /**
+ * @return a reference to the VelocityEngine used by all struts velocity thingies with the exception of
+ * directly accessed *.vm pages
+ */
+ public VelocityEngine getVelocityEngine() {
+ return velocityEngine;
+ }
+
+ /**
+ * This method is responsible for creating the standard VelocityContext used by all WW2 velocity views. The
+ * following context parameters are defined:
+ *
+ *
+ * request - the current HttpServletRequest
+ * response - the current HttpServletResponse
+ * stack - the current {@link ValueStack}
+ * ognl - an {@link OgnlTool}
+ * struts - an instance of {@link org.apache.struts2.util.StrutsUtil}
+ * action - the current Struts action
+ *
+ *
+ * @return a new StrutsVelocityContext
+ */
+ public Context createContext(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ VelocityContext[] chainedContexts = prepareChainedContexts(req, res, stack.getContext());
+ StrutsVelocityContext context = new StrutsVelocityContext(chainedContexts, stack);
+ Map standardMap = ContextUtil.getStandardContext(stack, req, res);
+ for (Iterator iterator = standardMap.entrySet().iterator(); iterator.hasNext();) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ context.put((String) entry.getKey(), entry.getValue());
+ }
+ context.put(STRUTS, new VelocityStrutsUtil(context, stack, req, res));
+
+
+ ServletContext ctx = null;
+ try {
+ ctx = ServletActionContext.getServletContext();
+ } catch (NullPointerException npe) {
+ // in case this was used outside the lifecycle of struts servlet
+ log.debug("internal toolbox context ignored");
+ }
+
+ if (toolboxManager != null && ctx != null) {
+ ChainedContext chained = new ChainedContext(context, req, res, ctx);
+ chained.setToolbox(toolboxManager.getToolboxContext(chained));
+ return chained;
+ } else {
+ return context;
+ }
+
+ }
+
+ /**
+ * constructs contexts for chaining on this request. This method does not
+ * perform any initialization of the contexts. All that must be done in the
+ * context itself.
+ *
+ * @param servletRequest
+ * @param servletResponse
+ * @param extraContext
+ * @return an VelocityContext[] of contexts to chain
+ */
+ protected VelocityContext[] prepareChainedContexts(HttpServletRequest servletRequest, HttpServletResponse servletResponse, Map extraContext) {
+ if (this.chainedContextNames == null) {
+ return null;
+ }
+ List contextList = new ArrayList();
+ for (int i = 0; i < chainedContextNames.length; i++) {
+ String className = chainedContextNames[i];
+ try {
+ VelocityContext velocityContext = (VelocityContext) ObjectFactory.getObjectFactory().buildBean(className, null);
+ contextList.add(velocityContext);
+ } catch (Exception e) {
+ log.warn("Warning. " + e.getClass().getName() + " caught while attempting to instantiate a chained VelocityContext, " + className + " -- skipping");
+ }
+ }
+ if (contextList.size() > 0) {
+ VelocityContext[] extraContexts = new VelocityContext[contextList.size()];
+ contextList.toArray(extraContexts);
+ return extraContexts;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * initializes the VelocityManager. this should be called during the initialization process, say by
+ * ServletDispatcher. this may be called multiple times safely although calls beyond the first won't do anything
+ *
+ * @param context the current servlet context
+ */
+ public synchronized void init(ServletContext context) {
+ if (velocityEngine == null) {
+ velocityEngine = newVelocityEngine(context);
+ }
+ this.initToolbox(context);
+ }
+
+ /**
+ * load optional velocity properties using the following loading strategy
+ *
+ * relative to the servlet context path
+ * relative to the WEB-INF directory
+ * on the classpath
+ *
+ *
+ * @param context the current ServletContext. may not be null
+ * @return the optional properties if struts.velocity.configfile was specified, an empty Properties file otherwise
+ */
+ public Properties loadConfiguration(ServletContext context) {
+ if (context == null) {
+ String gripe = "Error attempting to create a loadConfiguration from a null ServletContext!";
+ log.error(gripe);
+ throw new IllegalArgumentException(gripe);
+ }
+
+ Properties properties = new Properties();
+
+ // now apply our systemic defaults, then allow user to override
+ applyDefaultConfiguration(context, properties);
+
+
+ String defaultUserDirective = properties.getProperty("userdirective");
+
+ /**
+ * if the user has specified an external velocity configuration file, we'll want to search for it in the
+ * following order
+ *
+ * 1. relative to the context path
+ * 2. relative to /WEB-INF
+ * 3. in the class path
+ */
+ String configfile;
+
+ if (Settings.isSet(StrutsConstants.STRUTS_VELOCITY_CONFIGFILE)) {
+ configfile = Settings.get(StrutsConstants.STRUTS_VELOCITY_CONFIGFILE);
+ } else {
+ configfile = "velocity.properties";
+ }
+
+ configfile = configfile.trim();
+
+ InputStream in = null;
+ String resourceLocation = null;
+
+ try {
+ if (context.getRealPath(configfile) != null) {
+ // 1. relative to context path, i.e. /velocity.properties
+ String filename = context.getRealPath(configfile);
+
+ if (filename != null) {
+ File file = new File(filename);
+
+ if (file.isFile()) {
+ resourceLocation = file.getCanonicalPath() + " from file system";
+ in = new FileInputStream(file);
+ }
+
+ // 2. if nothing was found relative to the context path, search relative to the WEB-INF directory
+ if (in == null) {
+ file = new File(context.getRealPath("/WEB-INF/" + configfile));
+
+ if (file.isFile()) {
+ resourceLocation = file.getCanonicalPath() + " from file system";
+ in = new FileInputStream(file);
+ }
+ }
+ }
+ }
+
+ // 3. finally, if there's no physical file, how about something in our classpath
+ if (in == null) {
+ in = VelocityManager.class.getClassLoader().getResourceAsStream(configfile);
+ if (in != null) {
+ resourceLocation = configfile + " from classloader";
+ }
+ }
+
+ // if we've got something, load 'er up
+ if (in != null) {
+ log.info("Initializing velocity using " + resourceLocation);
+ properties.load(in);
+ }
+ } catch (IOException e) {
+ log.warn("Unable to load velocity configuration " + resourceLocation, e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ // overide with programmatically set properties
+ if (this.velocityProperties != null) {
+ Iterator keys = this.velocityProperties.keySet().iterator();
+ while (keys.hasNext()) {
+ String key = (String) keys.next();
+ properties.setProperty(key, this.velocityProperties.getProperty(key));
+ }
+ }
+
+ String userdirective = properties.getProperty("userdirective");
+
+ if ((userdirective == null) || userdirective.trim().equals("")) {
+ userdirective = defaultUserDirective;
+ } else {
+ userdirective = userdirective.trim() + "," + defaultUserDirective;
+ }
+
+ properties.setProperty("userdirective", userdirective);
+
+
+ // for debugging purposes, allows users to dump out the properties that have been configured
+ if (log.isDebugEnabled()) {
+ log.debug("Initializing Velocity with the following properties ...");
+
+ for (Iterator iter = properties.keySet().iterator();
+ iter.hasNext();) {
+ String key = (String) iter.next();
+ String value = properties.getProperty(key);
+
+ if (log.isDebugEnabled()) {
+ log.debug(" '" + key + "' = '" + value + "'");
+ }
+ }
+ }
+
+ return properties;
+ }
+
+ /**
+ * performs one-time initializations
+ */
+ protected void init() {
+
+ // read in the names of contexts to add to each request
+ initChainedContexts();
+
+
+ if (Settings.isSet(StrutsConstants.STRUTS_VELOCITY_TOOLBOXLOCATION)) {
+ toolBoxLocation = Settings.get(StrutsConstants.STRUTS_VELOCITY_TOOLBOXLOCATION).toString();
+ }
+
+ }
+
+
+ /**
+ * Initializes the ServletToolboxManager for this servlet's
+ * toolbox (if any).
+ */
+ protected void initToolbox(ServletContext context) {
+ /* if we have a toolbox, get a manager for it */
+ if (toolBoxLocation != null) {
+ toolboxManager = ServletToolboxManager.getInstance(context, toolBoxLocation);
+ } else {
+ Velocity.info("VelocityViewServlet: No toolbox entry in configuration.");
+ }
+ }
+
+
+ /**
+ * allow users to specify via the struts.properties file a set of additional VelocityContexts to chain to the
+ * the StrutsVelocityContext. The intent is to allow these contexts to store helper objects that the ui
+ * developer may want access to. Examples of reasonable VelocityContexts would be an IoCVelocityContext, a
+ * SpringReferenceVelocityContext, and a ToolboxVelocityContext
+ */
+ protected void initChainedContexts() {
+
+ if (Settings.isSet(StrutsConstants.STRUTS_VELOCITY_CONTEXTS)) {
+ // we expect contexts to be a comma separated list of classnames
+ String contexts = Settings.get(StrutsConstants.STRUTS_VELOCITY_CONTEXTS).toString();
+ StringTokenizer st = new StringTokenizer(contexts, ",");
+ List contextList = new ArrayList();
+
+ while (st.hasMoreTokens()) {
+ String classname = st.nextToken();
+ contextList.add(classname);
+ }
+ if (contextList.size() > 0) {
+ String[] chainedContexts = new String[contextList.size()];
+ contextList.toArray(chainedContexts);
+ this.chainedContextNames = chainedContexts;
+ }
+
+
+ }
+
+ }
+
+ /**
+ *
+ * Instantiates a new VelocityEngine.
+ *
+ *
+ * The following is the default Velocity configuration
+ *
+ *
+ * resource.loader = file, class
+ * file.resource.loader.path = real path of webapp
+ * class.resource.loader.description = Velocity Classpath Resource Loader
+ * class.resource.loader.class = org.apache.struts2.views.velocity.StrutsResourceLoader
+ *
+ *
+ * this default configuration can be overridden by specifying a struts.velocity.configfile property in the
+ * struts.properties file. the specified config file will be searched for in the following order:
+ *
+ *
+ * relative to the servlet context path
+ * relative to the WEB-INF directory
+ * on the classpath
+ *
+ *
+ * @param context the current ServletContext. may not be null
+ */
+ protected VelocityEngine newVelocityEngine(ServletContext context) {
+ if (context == null) {
+ String gripe = "Error attempting to create a new VelocityEngine from a null ServletContext!";
+ log.error(gripe);
+ throw new IllegalArgumentException(gripe);
+ }
+
+ Properties p = loadConfiguration(context);
+
+ VelocityEngine velocityEngine = new VelocityEngine();
+
+ // Set the velocity attribute for the servlet context
+ // if this is not set the webapp loader WILL NOT WORK
+ velocityEngine.setApplicationAttribute(ServletContext.class.getName(),
+ context);
+
+ try {
+ velocityEngine.init(p);
+ } catch (Exception e) {
+ String gripe = "Unable to instantiate VelocityEngine!";
+ throw new StrutsException(gripe, e);
+ }
+
+ return velocityEngine;
+ }
+
+ /**
+ * once we've loaded up the user defined configurations, we will want to apply Struts specification configurations.
+ *
+ * if Velocity.RESOURCE_LOADER has not been defined, then we will use the defaults which is a joined file,
+ * class loader for unpackaed wars and a straight class loader otherwise
+ * we need to define the various Struts custom user directives such as #param, #tag, and #bodytag
+ *
+ *
+ * @param context
+ * @param p
+ */
+ private void applyDefaultConfiguration(ServletContext context, Properties p) {
+ // ensure that caching isn't overly aggressive
+
+ /**
+ * Load a default resource loader definition if there isn't one present.
+ * Ben Hall (22/08/2003)
+ */
+ if (p.getProperty(Velocity.RESOURCE_LOADER) == null) {
+ p.setProperty(Velocity.RESOURCE_LOADER, "strutsfile, strutsclass");
+ }
+
+ /**
+ * If there's a "real" path add it for the strutsfile resource loader.
+ * If there's no real path and they haven't configured a loader then we change
+ * resource loader property to just use the strutsclass loader
+ * Ben Hall (22/08/2003)
+ */
+ if (context.getRealPath("") != null) {
+ p.setProperty("strutsfile.resource.loader.description", "Velocity File Resource Loader");
+ p.setProperty("strutsfile.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
+ p.setProperty("strutsfile.resource.loader.path", context.getRealPath(""));
+ p.setProperty("strutsfile.resource.loader.modificationCheckInterval", "2");
+ p.setProperty("strutsfile.resource.loader.cache", "true");
+ } else {
+ // remove strutsfile from resource loader property
+ String prop = p.getProperty(Velocity.RESOURCE_LOADER);
+ if (prop.indexOf("strutsfile,") != -1) {
+ prop = replace(prop, "strutsfile,", "");
+ } else if (prop.indexOf(", strutsfile") != -1) {
+ prop = replace(prop, ", strutsfile", "");
+ } else if (prop.indexOf("strutsfile") != -1) {
+ prop = replace(prop, "strutsfile", "");
+ }
+
+ p.setProperty(Velocity.RESOURCE_LOADER, prop);
+ }
+
+ /**
+ * Refactored the Velocity templates for the Struts taglib into the classpath from the web path. This will
+ * enable Struts projects to have access to the templates by simply including the Struts jar file.
+ * Unfortunately, there does not appear to be a macro for the class loader keywords
+ * Matt Ho - Mon Mar 17 00:21:46 PST 2003
+ */
+ p.setProperty("strutsclass.resource.loader.description", "Velocity Classpath Resource Loader");
+ p.setProperty("strutsclass.resource.loader.class", "org.apache.struts2.views.velocity.StrutsResourceLoader");
+ p.setProperty("strutsclass.resource.loader.modificationCheckInterval", "2");
+ p.setProperty("strutsclass.resource.loader.cache", "true");
+
+ // components
+ StringBuffer sb = new StringBuffer();
+
+ addDirective(sb, ActionDirective.class);
+ addDirective(sb, BeanDirective.class);
+ addDirective(sb, CheckBoxDirective.class);
+ addDirective(sb, CheckBoxListDirective.class);
+ addDirective(sb, ComboBoxDirective.class);
+ addDirective(sb, ComponentDirective.class);
+ addDirective(sb, DateDirective.class);
+ addDirective(sb, DatePickerDirective.class);
+ addDirective(sb, DivDirective.class);
+ addDirective(sb, DoubleSelectDirective.class);
+ addDirective(sb, FileDirective.class);
+ addDirective(sb, FormDirective.class);
+ addDirective(sb, HeadDirective.class);
+ addDirective(sb, HiddenDirective.class);
+ addDirective(sb, AnchorDirective.class);
+ addDirective(sb, I18nDirective.class);
+ addDirective(sb, IncludeDirective.class);
+ addDirective(sb, LabelDirective.class);
+ addDirective(sb, PanelDirective.class);
+ addDirective(sb, ParamDirective.class);
+ addDirective(sb, PasswordDirective.class);
+ addDirective(sb, PushDirective.class);
+ addDirective(sb, PropertyDirective.class);
+ addDirective(sb, RadioDirective.class);
+ addDirective(sb, SelectDirective.class);
+ addDirective(sb, SetDirective.class);
+ addDirective(sb, SubmitDirective.class);
+ addDirective(sb, ResetDirective.class);
+ addDirective(sb, TabbedPanelDirective.class);
+ addDirective(sb, TextAreaDirective.class);
+ addDirective(sb, TextDirective.class);
+ addDirective(sb, TextFieldDirective.class);
+ addDirective(sb, TokenDirective.class);
+ addDirective(sb, TreeDirective.class);
+ addDirective(sb, TreeNodeDirective.class);
+ addDirective(sb, URLDirective.class);
+ addDirective(sb, WebTableDirective.class);
+ addDirective(sb, ActionErrorDirective.class);
+ addDirective(sb, ActionMessageDirective.class);
+ addDirective(sb, FieldErrorDirective.class);
+ addDirective(sb, OptionTransferSelectDirective.class);
+ addDirective(sb, UpDownSelectDirective.class);
+
+ String directives = sb.toString();
+
+ String userdirective = p.getProperty("userdirective");
+ if ((userdirective == null) || userdirective.trim().equals("")) {
+ userdirective = directives;
+ } else {
+ userdirective = userdirective.trim() + "," + directives;
+ }
+
+ p.setProperty("userdirective", userdirective);
+ }
+
+ private void addDirective(StringBuffer sb, Class clazz) {
+ sb.append(clazz.getName()).append(",");
+ }
+
+ private static final String replace(String string, String oldString, String newString) {
+ if (string == null) {
+ return null;
+ }
+ // If the newString is null, just return the string since there's nothing to replace.
+ if (newString == null) {
+ return string;
+ }
+ int i = 0;
+ // Make sure that oldString appears at least once before doing any processing.
+ if ((i = string.indexOf(oldString, i)) >= 0) {
+ // Use char []'s, as they are more efficient to deal with.
+ char[] string2 = string.toCharArray();
+ char[] newString2 = newString.toCharArray();
+ int oLength = oldString.length();
+ StringBuffer buf = new StringBuffer(string2.length);
+ buf.append(string2, 0, i).append(newString2);
+ i += oLength;
+ int j = i;
+ // Replace all remaining instances of oldString with newString.
+ while ((i = string.indexOf(oldString, i)) > 0) {
+ buf.append(string2, j, i - j).append(newString2);
+ i += oLength;
+ j = i;
+ }
+ buf.append(string2, j, string2.length - j);
+ return buf.toString();
+ }
+ return string;
+ }
+
+ /**
+ * @return the velocityProperties
+ */
+ public Properties getVelocityProperties() {
+ return velocityProperties;
+ }
+
+ /**
+ * @param velocityProperties the velocityProperties to set
+ */
+ public void setVelocityProperties(Properties velocityProperties) {
+ this.velocityProperties = velocityProperties;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java
new file mode 100644
index 000000000..633e00297
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java
@@ -0,0 +1,126 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.components.Component;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.directive.Directive;
+import org.apache.velocity.runtime.parser.node.Node;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+public abstract class AbstractDirective extends Directive {
+ public String getName() {
+ return "s" + getBeanName();
+ }
+
+ public abstract String getBeanName();
+
+ /**
+ * All components, unless otherwise stated, are LINE-level directives.
+ */
+ public int getType() {
+ return LINE;
+ }
+
+ protected abstract Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res);
+
+ public boolean render(InternalContextAdapter ctx, Writer writer, Node node) throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException {
+ // get the bean
+ ValueStack stack = (ValueStack) ctx.get("stack");
+ HttpServletRequest req = (HttpServletRequest) stack.getContext().get(ServletActionContext.HTTP_REQUEST);
+ HttpServletResponse res = (HttpServletResponse) stack.getContext().get(ServletActionContext.HTTP_RESPONSE);
+ Component bean = getBean(stack, req, res);
+
+ // get the parameters
+ Map params = createPropertyMap(ctx, node);
+ bean.copyParams(params);
+ //bean.addAllParameters(params);
+ bean.start(writer);
+
+ if (getType() == BLOCK) {
+ Node body = node.jjtGetChild(node.jjtGetNumChildren() - 1);
+ body.render(ctx, writer);
+ }
+
+ bean.end(writer, "");
+ return true;
+ }
+
+ /**
+ * create a Map of properties that the user has passed in. for example,
+ *
+ * #xxx("name=hello" "value=world" "template=foo")
+ *
+ * would yield a params that contains {["name", "hello"], ["value", "world"], ["template", "foo"]}
+ *
+ * @param node the Node passed in to the render method
+ * @return a Map of the user specified properties
+ * @throws org.apache.velocity.exception.ParseErrorException
+ * if the was an error in the format of the property
+ */
+ protected Map createPropertyMap(InternalContextAdapter contextAdapter, Node node) throws ParseErrorException, MethodInvocationException {
+ Map propertyMap = new HashMap();
+
+ int children = node.jjtGetNumChildren();
+ if (getType() == BLOCK) {
+ children--;
+ }
+
+ for (int index = 0, length = children; index < length; index++) {
+ this.putProperty(propertyMap, contextAdapter, node.jjtGetChild(index));
+ }
+
+ return propertyMap;
+ }
+
+ /**
+ * adds a given Node's key/value pair to the propertyMap. For example, if this Node contained the value "rows=20",
+ * then the key, rows, would be added to the propertyMap with the String value, 20.
+ *
+ * @param propertyMap a params containing all the properties that we wish to set
+ * @param node the parameter to set expressed in "name=value" format
+ */
+ protected void putProperty(Map propertyMap, InternalContextAdapter contextAdapter, Node node) throws ParseErrorException, MethodInvocationException {
+ // node.value uses the StrutsValueStack to evaluate the directive's value parameter
+ String param = node.value(contextAdapter).toString();
+
+ int idx = param.indexOf("=");
+
+ if (idx != -1) {
+ String property = param.substring(0, idx);
+
+ String value = param.substring(idx + 1);
+ propertyMap.put(property, value);
+ } else {
+ throw new ParseErrorException("#" + this.getName() + " arguments must include an assignment operator! For example #tag( Component \"template=mytemplate\" ). #tag( TextField \"mytemplate\" ) is illegal!");
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ActionDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ActionDirective.java
new file mode 100644
index 000000000..a0b4eb112
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ActionDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ActionComponent;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ActionComponent
+ */
+public class ActionDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "action";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new ActionComponent(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ActionErrorDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ActionErrorDirective.java
new file mode 100644
index 000000000..435e1c85f
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ActionErrorDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ActionError;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ActionError
+ */
+public class ActionErrorDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "actionerror";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new ActionError(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ActionMessageDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ActionMessageDirective.java
new file mode 100644
index 000000000..0e56e7433
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ActionMessageDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ActionMessage;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ActionMessage
+ */
+public class ActionMessageDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "actionmessage";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new ActionMessage(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/AnchorDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/AnchorDirective.java
new file mode 100644
index 000000000..fbf9db252
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/AnchorDirective.java
@@ -0,0 +1,43 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Anchor;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Anchor
+ */
+public class AnchorDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "a";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Anchor(stack, req, res);
+ }
+
+ public int getType() {
+ return BLOCK;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/BeanDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/BeanDirective.java
new file mode 100644
index 000000000..38ca4bdca
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/BeanDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Bean;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Bean
+ */
+public class BeanDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "bean";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Bean(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/CheckBoxDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/CheckBoxDirective.java
new file mode 100644
index 000000000..664857bb8
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/CheckBoxDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Checkbox;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Checkbox
+ */
+public class CheckBoxDirective extends AbstractDirective {
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Checkbox(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "checkbox";
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/CheckBoxListDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/CheckBoxListDirective.java
new file mode 100644
index 000000000..1fb6fb896
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/CheckBoxListDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.CheckboxList;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see CheckboxList
+ */
+public class CheckBoxListDirective extends AbstractDirective {
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new CheckboxList(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "checkboxlist";
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ComboBoxDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ComboBoxDirective.java
new file mode 100644
index 000000000..124c4136f
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ComboBoxDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.ComboBox;
+import org.apache.struts2.components.Component;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see ComboBox
+ */
+public class ComboBoxDirective extends AbstractDirective {
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new ComboBox(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "combobox";
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ComponentDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ComponentDirective.java
new file mode 100644
index 000000000..3d295d727
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ComponentDirective.java
@@ -0,0 +1,43 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.GenericUIBean;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see GenericUIBean
+ */
+public class ComponentDirective extends AbstractDirective {
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new GenericUIBean(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "component";
+ }
+
+ public int getType() {
+ return BLOCK;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DateDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DateDirective.java
new file mode 100644
index 000000000..28ac7088e
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DateDirective.java
@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Date;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * DateDirective
+ */
+public class DateDirective extends AbstractDirective {
+
+ public String getBeanName() {
+ return "date";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Date(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DatePickerDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DatePickerDirective.java
new file mode 100644
index 000000000..77aa7e227
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DatePickerDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.DatePicker;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see DatePicker
+ */
+public class DatePickerDirective extends TextFieldDirective {
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new DatePicker(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "datepicker";
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DivDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DivDirective.java
new file mode 100644
index 000000000..0a8da4927
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DivDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Div;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Div
+ */
+public class DivDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "div";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Div(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DoubleSelectDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DoubleSelectDirective.java
new file mode 100644
index 000000000..29015bebe
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/DoubleSelectDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.DoubleSelect;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see DoubleSelect
+ */
+public class DoubleSelectDirective extends AbstractDirective {
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new DoubleSelect(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "doubleselect";
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/FieldErrorDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/FieldErrorDirective.java
new file mode 100644
index 000000000..799f1a8c8
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/FieldErrorDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.FieldError;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see FieldError
+ */
+public class FieldErrorDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "fielderror";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new FieldError(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/FileDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/FileDirective.java
new file mode 100644
index 000000000..b4b7f0ba9
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/FileDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.File;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see File
+ */
+public class FileDirective extends AbstractDirective {
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new File(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "file";
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/FormDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/FormDirective.java
new file mode 100644
index 000000000..60361a787
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/FormDirective.java
@@ -0,0 +1,43 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Form;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Form
+ */
+public class FormDirective extends AbstractDirective {
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Form(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "form";
+ }
+
+ public int getType() {
+ return BLOCK;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/HeadDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/HeadDirective.java
new file mode 100644
index 000000000..974d264f2
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/HeadDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Head;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Head
+ */
+public class HeadDirective extends AbstractDirective {
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Head(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "head";
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/HiddenDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/HiddenDirective.java
new file mode 100644
index 000000000..dd4a946f0
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/HiddenDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Hidden;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Hidden
+ */
+public class HiddenDirective extends AbstractDirective {
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Hidden(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "hidden";
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/I18nDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/I18nDirective.java
new file mode 100644
index 000000000..922508fa9
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/I18nDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.I18n;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see I18n
+ */
+public class I18nDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "i18n";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new I18n(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/IncludeDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/IncludeDirective.java
new file mode 100644
index 000000000..3219a0a92
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/IncludeDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Include;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Include
+ */
+public class IncludeDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "include";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Include(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/LabelDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/LabelDirective.java
new file mode 100644
index 000000000..20c566579
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/LabelDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Label;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Label
+ */
+public class LabelDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "label";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Label(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/OptGroupDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/OptGroupDirective.java
new file mode 100644
index 000000000..36b4dae2d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/OptGroupDirective.java
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.OptGroup;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * OptGroup velocity directive.
+ */
+public class OptGroupDirective extends AbstractDirective {
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new OptGroup(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "optgroup";
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/OptionTransferSelectDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/OptionTransferSelectDirective.java
new file mode 100644
index 000000000..0ac1745aa
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/OptionTransferSelectDirective.java
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.OptionTransferSelect;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see OptionTransferSelect
+ */
+public class OptionTransferSelectDirective extends AbstractDirective {
+
+ public String getBeanName() {
+ return "optiontransferselect";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new OptionTransferSelect(stack, req, res);
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PanelDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PanelDirective.java
new file mode 100644
index 000000000..f62239f87
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PanelDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Panel;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Panel
+ */
+public class PanelDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "panel";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Panel(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ParamDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ParamDirective.java
new file mode 100644
index 000000000..7c0ef6da1
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ParamDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Param;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Param
+ */
+public class ParamDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "param";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Param(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PasswordDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PasswordDirective.java
new file mode 100644
index 000000000..d9b396cda
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PasswordDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Password;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Password
+ */
+public class PasswordDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "password";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Password(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PropertyDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PropertyDirective.java
new file mode 100644
index 000000000..32913385d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PropertyDirective.java
@@ -0,0 +1,38 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Property;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ */
+public class PropertyDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "property";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Property(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PushDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PushDirective.java
new file mode 100644
index 000000000..91d533967
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/PushDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Push;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Push
+ */
+public class PushDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "push";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Push(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/RadioDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/RadioDirective.java
new file mode 100644
index 000000000..14289cd31
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/RadioDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Radio;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Radio
+ */
+public class RadioDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "radio";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Radio(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ResetDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ResetDirective.java
new file mode 100644
index 000000000..73e62fac5
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/ResetDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Reset;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see org.apache.struts2.components.Reset
+ */
+public class ResetDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "reset";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Reset(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/SelectDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/SelectDirective.java
new file mode 100644
index 000000000..cd57a2c67
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/SelectDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Select;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Select
+ */
+public class SelectDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "select";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Select(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/SetDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/SetDirective.java
new file mode 100644
index 000000000..2e9ce6717
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/SetDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Set;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Set
+ */
+public class SetDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "set";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Set(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/SubmitDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/SubmitDirective.java
new file mode 100644
index 000000000..40b99c560
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/SubmitDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Submit;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Submit
+ */
+public class SubmitDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "submit";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Submit(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TabbedPanelDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TabbedPanelDirective.java
new file mode 100644
index 000000000..9a256a4c1
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TabbedPanelDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TabbedPanel;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see TabbedPanel
+ */
+public class TabbedPanelDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "tabbedpanel";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new TabbedPanel(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TextAreaDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TextAreaDirective.java
new file mode 100644
index 000000000..8315da596
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TextAreaDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TextArea;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see TextArea
+ */
+public class TextAreaDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "textarea";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new TextArea(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TextDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TextDirective.java
new file mode 100644
index 000000000..a3def019b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TextDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Text;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Text
+ */
+public class TextDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "text";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Text(stack);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TextFieldDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TextFieldDirective.java
new file mode 100644
index 000000000..d1dcb028e
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TextFieldDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TextField;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see TextField
+ */
+public class TextFieldDirective extends AbstractDirective {
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new TextField(stack, req, res);
+ }
+
+ public String getBeanName() {
+ return "textfield";
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TokenDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TokenDirective.java
new file mode 100644
index 000000000..0002eec2c
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TokenDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Token;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see Token
+ */
+public class TokenDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "token";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Token(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TreeDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TreeDirective.java
new file mode 100644
index 000000000..bdcfb1e8d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TreeDirective.java
@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.Tree;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * TreeDirective
+ * @see Tree
+ */
+public class TreeDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "tree";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new Tree(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TreeNodeDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TreeNodeDirective.java
new file mode 100644
index 000000000..3f6392c37
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/TreeNodeDirective.java
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.TreeNode;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * TreeNodeDirective
+ * @see TreeNode
+ */
+public class TreeNodeDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "treenode";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new TreeNode(stack, req, res);
+ }
+}
+
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/URLDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/URLDirective.java
new file mode 100644
index 000000000..bed53d87e
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/URLDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.URL;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see URL
+ */
+public class URLDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "url";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new URL(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/UpDownSelectDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/UpDownSelectDirective.java
new file mode 100644
index 000000000..d5cb969ea
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/UpDownSelectDirective.java
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.UpDownSelect;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see UpDownSelect
+ *
+ */
+public class UpDownSelectDirective extends AbstractDirective {
+
+ public String getBeanName() {
+ return "updownselect";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new UpDownSelect(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/WebTableDirective.java b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/WebTableDirective.java
new file mode 100644
index 000000000..e9570f453
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/components/WebTableDirective.java
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.velocity.components;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.table.WebTable;
+
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ * @see WebTable
+ */
+public class WebTableDirective extends AbstractDirective {
+ public String getBeanName() {
+ return "table";
+ }
+
+ protected Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
+ return new WebTable(stack, req, res);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/velocity/package.html b/trunk/core/src/main/java/org/apache/struts2/views/velocity/package.html
new file mode 100644
index 000000000..82188fd64
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/velocity/package.html
@@ -0,0 +1 @@
+Classes for views using Velocity.
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/AbstractAdapterElement.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/AbstractAdapterElement.java
new file mode 100644
index 000000000..5e5ed16c9
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/AbstractAdapterElement.java
@@ -0,0 +1,137 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.TypeInfo;
+
+/**
+ * AbstractAdapterElement extends the abstract Node type and implements
+ * the DOM Element interface.
+ */
+public abstract class AbstractAdapterElement extends AbstractAdapterNode implements Element {
+
+ private Map attributeAdapters;
+
+ public AbstractAdapterElement() { }
+
+ public void setAttribute(String string, String string1) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ protected Map getAttributeAdapters() {
+ if ( attributeAdapters == null )
+ attributeAdapters = buildAttributeAdapters();
+ return attributeAdapters;
+ }
+
+ protected Map buildAttributeAdapters() {
+ return new HashMap();
+ }
+
+ /**
+ * No attributes, return empty attributes if asked.
+ */
+ public String getAttribute(String string) {
+ return "";
+ }
+
+ public void setAttributeNS(String string, String string1, String string2) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public String getAttributeNS(String string, String string1) {
+ return null;
+ }
+
+ public Attr setAttributeNode(Attr attr) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public Attr getAttributeNode( String name ) {
+ return (Attr)getAttributes().getNamedItem( name );
+ }
+
+ public Attr setAttributeNodeNS(Attr attr) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public Attr getAttributeNodeNS(String string, String string1) {
+ throw operationNotSupported();
+ }
+
+ public String getNodeName() {
+ return getTagName();
+ }
+
+ public short getNodeType() {
+ return Node.ELEMENT_NODE;
+ }
+
+ public String getTagName() {
+ return getPropertyName();
+ }
+
+ public boolean hasAttribute(String string) {
+ return false;
+ }
+
+ public boolean hasAttributeNS(String string, String string1) {
+ return false;
+ }
+
+ public boolean hasChildNodes() {
+ return getElementsByTagName("*").getLength() > 0;
+ }
+
+ public void removeAttribute(String string) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public void removeAttributeNS(String string, String string1) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public Attr removeAttributeNode(Attr attr) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public void setIdAttributeNode(Attr attr, boolean b) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public TypeInfo getSchemaTypeInfo() {
+ throw operationNotSupported();
+ }
+
+ public void setIdAttribute(String string, boolean b) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public void setIdAttributeNS(String string, String string1, boolean b) throws DOMException {
+ throw operationNotSupported();
+ }
+
+}
+
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/AbstractAdapterNode.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/AbstractAdapterNode.java
new file mode 100644
index 000000000..5ce4f3cf8
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/AbstractAdapterNode.java
@@ -0,0 +1,380 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.StrutsException;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.UserDataHandler;
+
+/**
+ * AbstractAdapterNode is the base for childAdapters that expose a read-only view
+ * of a Java object as a DOM Node. This class implements the core parent-child
+ * and sibling node traversal functionality shared by all adapter type nodes
+ * and used in proxy node support.
+ *
+ * @see AbstractAdapterElement
+ */
+public abstract class AbstractAdapterNode implements AdapterNode {
+
+ private static final NamedNodeMap EMPTY_NAMEDNODEMAP =
+ new NamedNodeMap() {
+ public int getLength() {
+ return 0;
+ }
+
+ public Node item(int index) {
+ return null;
+ }
+
+ public Node getNamedItem(String name) {
+ return null;
+ }
+
+ public Node removeNamedItem(String name) throws DOMException {
+ return null;
+ }
+
+ public Node setNamedItem(Node arg) throws DOMException {
+ return null;
+ }
+
+ public Node setNamedItemNS(Node arg) throws DOMException {
+ return null;
+ }
+
+ public Node getNamedItemNS(String namespaceURI, String localName) {
+ return null;
+ }
+
+ public Node removeNamedItemNS(String namespaceURI, String localName) throws DOMException {
+ return null;
+ }
+ };
+
+ private List childAdapters;
+ private Log log = LogFactory.getLog(this.getClass());
+
+ // The domain object that we are adapting
+ private Object propertyValue;
+ private String propertyName;
+ private AdapterNode parent;
+ private AdapterFactory adapterFactory;
+
+
+ public AbstractAdapterNode() {
+ if (LogFactory.getLog(getClass()).isDebugEnabled()) {
+ LogFactory.getLog(getClass()).debug("Creating " + this);
+ }
+ }
+
+ /**
+ *
+ * @param adapterFactory
+ * @param parent
+ * @param propertyName
+ * @param value
+ */
+ protected void setContext(AdapterFactory adapterFactory, AdapterNode parent, String propertyName, Object value) {
+ setAdapterFactory(adapterFactory);
+ setParent(parent);
+ setPropertyName(propertyName);
+ setPropertyValue(value);
+ }
+
+ /**
+ * subclasses override to produce their children
+ *
+ * @return List of child adapters.
+ */
+ protected List buildChildAdapters() {
+ return new ArrayList();
+ }
+
+ /**
+ * Lazily initialize child childAdapters
+ */
+ protected List getChildAdapters() {
+ if (childAdapters == null) {
+ childAdapters = buildChildAdapters();
+ }
+ return childAdapters;
+ }
+
+ public Node getChildBeforeOrAfter(Node child, boolean before) {
+ log.debug("getChildBeforeOrAfter: ");
+ List adapters = getChildAdapters();
+ log.debug("childAdapters = " + adapters);
+ log.debug("child = " + child);
+ int index = adapters.indexOf(child);
+ if (index < 0)
+ throw new StrutsException(child + " is no child of " + this);
+ int siblingIndex = before ? index - 1 : index + 1;
+ return ((0 < siblingIndex) && (siblingIndex < adapters.size())) ?
+ ((Node) adapters.get(siblingIndex)) : null;
+ }
+
+ public Node getChildAfter(Node child) {
+ log.trace("getChildafter");
+ return getChildBeforeOrAfter(child, false/*after*/);
+ }
+
+ public Node getChildBefore(Node child) {
+ log.trace("getchildbefore");
+ return getChildBeforeOrAfter(child, true/*after*/);
+ }
+
+ public NodeList getElementsByTagName(String tagName) {
+ if (tagName.equals("*")) {
+ return getChildNodes();
+ } else {
+ LinkedList filteredChildren = new LinkedList();
+
+ for (Node adapterNode : getChildAdapters()) {
+ if (adapterNode.getNodeName().equals(tagName)) {
+ filteredChildren.add(adapterNode);
+ }
+ }
+
+ return new SimpleNodeList(filteredChildren);
+ }
+ }
+
+ public NodeList getElementsByTagNameNS(String string, String string1) {
+ // TODO:
+ return null;
+ }
+
+ // Begin Node methods
+
+ public NamedNodeMap getAttributes() {
+ return EMPTY_NAMEDNODEMAP;
+ }
+
+ public NodeList getChildNodes() {
+ NodeList nl = new SimpleNodeList(getChildAdapters());
+ if (log.isDebugEnabled())
+ log.debug("getChildNodes for tag: "
+ + getNodeName() + " num children: " + nl.getLength());
+ return nl;
+ }
+
+ public Node getFirstChild() {
+ return (getChildNodes().getLength() > 0) ? getChildNodes().item(0) : null;
+ }
+
+ public Node getLastChild() {
+ return (getChildNodes().getLength() > 0) ? getChildNodes().item(getChildNodes().getLength() - 1) : null;
+ }
+
+
+ public String getLocalName() {
+ return null;
+ }
+
+ public String getNamespaceURI() {
+ return null;
+ }
+
+ public void setNodeValue(String string) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public String getNodeValue() throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public Document getOwnerDocument() {
+ return null;
+ }
+
+ public Node getParentNode() {
+ log.trace("getParentNode");
+ return getParent();
+ }
+
+ public AdapterNode getParent() {
+ return parent;
+ }
+
+ public void setParent(AdapterNode parent) {
+ this.parent = parent;
+ }
+
+ public Object getPropertyValue() {
+ return propertyValue;
+ }
+
+ public void setPropertyValue(Object prop) {
+ this.propertyValue = prop;
+ }
+
+ public void setPrefix(String string) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public String getPrefix() {
+ return null;
+ }
+
+ public Node getNextSibling() {
+ Node next = getParent().getChildAfter(this);
+ if (log.isTraceEnabled()) {
+ log.trace("getNextSibling on " + getNodeName() + ": "
+ + ((next == null) ? "null" : next.getNodeName()));
+ }
+
+ return getParent().getChildAfter(this);
+ }
+
+ public Node getPreviousSibling() {
+ return getParent().getChildBefore(this);
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ public void setPropertyName(String name) {
+ this.propertyName = name;
+ }
+
+ public AdapterFactory getAdapterFactory() {
+ return adapterFactory;
+ }
+
+ public void setAdapterFactory(AdapterFactory adapterFactory) {
+ this.adapterFactory = adapterFactory;
+ }
+
+ public boolean isSupported(String string, String string1) {
+ throw operationNotSupported();
+ }
+
+ public Node appendChild(Node node) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public Node cloneNode(boolean b) {
+ log.trace("cloneNode");
+ throw operationNotSupported();
+ }
+
+ public boolean hasAttributes() {
+ return false;
+ }
+
+ public boolean hasChildNodes() {
+ return false;
+ }
+
+ public Node insertBefore(Node node, Node node1) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public void normalize() {
+ log.trace("normalize");
+ throw operationNotSupported();
+ }
+
+ public Node removeChild(Node node) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public Node replaceChild(Node node, Node node1) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ // Begin DOM 3 methods
+
+ public boolean isDefaultNamespace(String string) {
+ throw operationNotSupported();
+ }
+
+ public String lookupNamespaceURI(String string) {
+ throw operationNotSupported();
+ }
+
+ public String getNodeName() {
+ throw operationNotSupported();
+ }
+
+ public short getNodeType() {
+ throw operationNotSupported();
+ }
+
+ public String getBaseURI() {
+ throw operationNotSupported();
+ }
+
+ public short compareDocumentPosition(Node node) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public String getTextContent() throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public void setTextContent(String string) throws DOMException {
+ throw operationNotSupported();
+
+ }
+
+ public boolean isSameNode(Node node) {
+ throw operationNotSupported();
+ }
+
+ public String lookupPrefix(String string) {
+ throw operationNotSupported();
+ }
+
+ public boolean isEqualNode(Node node) {
+ throw operationNotSupported();
+ }
+
+ public Object getFeature(String string, String string1) {
+ throw operationNotSupported();
+ }
+
+ public Object setUserData(String string, Object object, UserDataHandler userDataHandler) {
+ throw operationNotSupported();
+ }
+
+ public Object getUserData(String string) {
+ throw operationNotSupported();
+ }
+
+ // End node methods
+
+ protected StrutsException operationNotSupported() {
+ return new StrutsException("Operation not supported.");
+ }
+
+ public String toString() {
+ return getClass() + ": " + getNodeName() + " parent=" + getParentNode();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/AdapterFactory.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/AdapterFactory.java
new file mode 100644
index 000000000..f3cb037a4
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/AdapterFactory.java
@@ -0,0 +1,239 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.struts2.StrutsException;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+/**
+ * AdapterFactory produces Node adapters for Java object types.
+ * Adapter classes are generally instantiated dynamically via a no-args constructor
+ * and populated with their context information via the AdapterNode interface.
+ *
+ * This factory supports proxying of generic DOM Node trees, allowing arbitrary
+ * Node types to be mixed together. You may simply return a Document or Node
+ * type as an object property and it will appear as a sub-tree in the XML as
+ * you'd expect. See #proxyNode().
+ *
+ * Customization of the result XML can be accomplished by providing
+ * alternate adapters for Java types. Adapters are associated with Java
+ * types through the registerAdapterType() method.
+ *
+ * For example, since there is no default Date adapter, Date objects will be
+ * rendered with the generic Bean introspecting adapter, producing output
+ * like:
+ *
+
+ 19
+ 1
+ 0
+ 7
+ 8
+ 4
+ 1127106424531
+ 300
+ 105
+
+ *
+ *
+ * By extending the StringAdapter and overriding its normal behavior we can
+ * create a custom Date formatter:
+ *
+ *
+ public static class CustomDateAdapter extends StringAdapter {
+ protected String getStringValue() {
+ Date date = (Date)getPropertyValue();
+ return DateFormat.getTimeInstance( DateFormat.FULL ).format( date );
+ }
+ }
+ *
+ *
+ * Producing output like:
+ *
+
+ 12:02:54 AM CDT
+
+ *
+ * The StringAdapter (which is normally invoked only to adapt String values)
+ * is a useful base for these kinds of customizations and can produce
+ * structured XML output as well as plain text by setting its parseStringAsXML()
+ * property to true.
+ *
+ * See provided examples.
+ */
+public class AdapterFactory {
+
+ /**
+ * Map>
+ */
+ private Map adapterTypes = new HashMap();
+
+ /**
+ * Register an adapter type for a Java class type.
+ *
+ * @param type the Java class type which is to be handled by the adapter.
+ * @param adapterType The adapter class, which implements AdapterNode.
+ */
+ public void registerAdapterType(Class type, Class adapterType) {
+ adapterTypes.put(type, adapterType);
+ }
+
+ /**
+ * Create a top level Document adapter for the specified Java object.
+ * The document will have a root element with the specified property name
+ * and contain the specified Java object content.
+ *
+ * @param propertyName The name of the root document element
+ * @return
+ * @throws IllegalAccessException
+ * @throws InstantiationException
+ */
+ public Document adaptDocument(String propertyName, Object propertyValue)
+ throws IllegalAccessException, InstantiationException {
+ //if ( propertyValue instanceof Document )
+ // return (Document)propertyValue;
+
+ return new SimpleAdapterDocument(this, null, propertyName, propertyValue);
+ }
+
+
+ /**
+ * Create an Node adapter for a child element.
+ * Note that the parent of the created node must be an AdapterNode, however
+ * the child node itself may be any type of Node.
+ *
+ * @see #adaptDocument( String, Object )
+ */
+ public Node adaptNode(AdapterNode parent, String propertyName, Object value) {
+ Class adapterClass = getAdapterForValue(value);
+ if (adapterClass != null)
+ return constructAdapterInstance(adapterClass, parent, propertyName, value);
+
+ // If the property is a Document, "unwrap" it to the root element
+ if (value instanceof Document)
+ value = ((Document) value).getDocumentElement();
+
+ // If the property is already a Node, proxy it
+ if (value instanceof Node)
+ return proxyNode(parent, (Node) value);
+
+ // Check other supported types or default to generic JavaBean introspecting adapter
+ Class valueType = value.getClass();
+
+ if (valueType.isArray())
+ adapterClass = ArrayAdapter.class;
+ else if (value instanceof String || value instanceof Number || valueType.isPrimitive())
+ adapterClass = StringAdapter.class;
+ else if (value instanceof Collection)
+ adapterClass = CollectionAdapter.class;
+ else if (value instanceof Map)
+ adapterClass = MapAdapter.class;
+ else
+ adapterClass = BeanAdapter.class;
+
+ return constructAdapterInstance(adapterClass, parent, propertyName, value);
+ }
+
+ /**
+ * Construct a proxy adapter for a value that is an existing DOM Node.
+ * This allows arbitrary DOM Node trees to be mixed in with our results.
+ * The proxied nodes are read-only and currently support only
+ * limited types of Nodes including Element, Text, and Attributes. (Other
+ * Node types may be ignored by the proxy and not appear in the result tree).
+ *
+ * // TODO:
+ * NameSpaces are not yet supported.
+ *
+ * This method is primarily for use by the adapter node classes.
+ */
+ public Node proxyNode(AdapterNode parent, Node node) {
+ // If the property is a Document, "unwrap" it to the root element
+ if (node instanceof Document)
+ node = ((Document) node).getDocumentElement();
+
+ if (node == null)
+ return null;
+ if (node.getNodeType() == Node.ELEMENT_NODE)
+ return new ProxyElementAdapter(this, parent, (Element) node);
+ if (node.getNodeType() == Node.TEXT_NODE)
+ return new ProxyTextNodeAdapter(this, parent, (Text) node);
+ if (node.getNodeType() == Node.ATTRIBUTE_NODE)
+ return new ProxyAttrAdapter(this, parent, (Attr) node);
+
+ return null; // Unsupported Node type - ignore for now
+ }
+
+ public NamedNodeMap proxyNamedNodeMap(AdapterNode parent, NamedNodeMap nnm) {
+ return new ProxyNamedNodeMap(this, parent, nnm);
+ }
+
+ /**
+ * Create an instance of an adapter dynamically and set its context via
+ * the AdapterNode interface.
+ */
+ private Node constructAdapterInstance(Class adapterClass, AdapterNode parent, String propertyName, Object propertyValue) {
+ // Check to see if the class has a no-args constructor
+ try {
+ adapterClass.getConstructor(new Class []{});
+ } catch (NoSuchMethodException e1) {
+ throw new StrutsException("Adapter class: " + adapterClass
+ + " does not have a no-args consructor.");
+ }
+
+ try {
+ AdapterNode adapterNode = (AdapterNode) adapterClass.newInstance();
+ adapterNode.setAdapterFactory(this);
+ adapterNode.setParent(parent);
+ adapterNode.setPropertyName(propertyName);
+ adapterNode.setPropertyValue(propertyValue);
+
+ return adapterNode;
+
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ throw new StrutsException("Cannot adapt " + propertyValue + " (" + propertyName + ") :" + e.getMessage());
+ } catch (InstantiationException e) {
+ e.printStackTrace();
+ throw new StrutsException("Cannot adapt " + propertyValue + " (" + propertyName + ") :" + e.getMessage());
+ }
+ }
+
+ /**
+ * Create an appropriate adapter for a null value.
+ *
+ * @param parent
+ * @param propertyName
+ */
+ public Node adaptNullValue(BeanAdapter parent, String propertyName) {
+ return new StringAdapter(this, parent, propertyName, "null");
+ }
+
+ //TODO: implement Configuration option to provide additional adapter classes
+ public Class getAdapterForValue(Object value) {
+ return adapterTypes.get(value.getClass());
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/AdapterNode.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/AdapterNode.java
new file mode 100644
index 000000000..fa6c70fc8
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/AdapterNode.java
@@ -0,0 +1,75 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import org.w3c.dom.Node;
+
+/**
+ */
+public interface AdapterNode extends Node {
+
+ /**
+ * The adapter factory that created this node.
+ */
+ AdapterFactory getAdapterFactory();
+
+ /**
+ * The adapter factory that created this node.
+ */
+ void setAdapterFactory(AdapterFactory factory);
+
+ /**
+ * The parent adapter node of this node. Note that our parent must be another adapter node, but our children may be any
+ * kind of Node.
+ */
+ AdapterNode getParent();
+
+ /**
+ * The parent adapter node of this node. Note that our parent must be another adapter node, but our children may be any
+ * kind of Node.
+ */
+ void setParent(AdapterNode parent);
+
+ /**
+ * The child node before the specified sibling
+ */
+ Node getChildBefore(Node thisNode);
+
+ /**
+ * The child node after the specified sibling
+ */
+ Node getChildAfter(Node thisNode);
+
+ /**
+ * The name of the Java object (property) that we are adapting
+ */
+ String getPropertyName();
+
+ /**
+ * The name of the Java object (property) that we are adapting
+ */
+ void setPropertyName(String name);
+
+ /**
+ * The Java object (property) that we are adapting
+ */
+ Object getPropertyValue();
+
+ /** The Java object (property) that we are adapting */
+ void setPropertyValue(Object prop );
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/ArrayAdapter.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ArrayAdapter.java
new file mode 100644
index 000000000..a434d63a8
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ArrayAdapter.java
@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.w3c.dom.Node;
+
+
+/**
+ */
+public class ArrayAdapter extends AbstractAdapterElement {
+
+ private Log log = LogFactory.getLog(this.getClass());
+
+ public ArrayAdapter() {
+ }
+
+ public ArrayAdapter(AdapterFactory adapterFactory, AdapterNode parent, String propertyName, Object value) {
+ setContext(adapterFactory, parent, propertyName, value);
+ }
+
+ protected List buildChildAdapters() {
+ List children = new ArrayList();
+ Object[] values = (Object[]) getPropertyValue();
+
+ for (Object value : values) {
+ Node childAdapter = getAdapterFactory().adaptNode(this, "item", value);
+ if (childAdapter != null)
+ children.add(childAdapter);
+
+ if (log.isDebugEnabled()) {
+ log.debug(this + " adding adapter: " + childAdapter);
+ }
+ }
+
+ return children;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/BeanAdapter.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/BeanAdapter.java
new file mode 100644
index 000000000..822c84d0d
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/BeanAdapter.java
@@ -0,0 +1,172 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.StrutsException;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+/**
+ * This class is the most general type of adapter, utilizing reflective introspection to present a DOM view of all of
+ * the public properties of its value. For example, a property returning a JavaBean such as:
+ *
+ *
+ * public Person getMyPerson() { ... }
+ * ...
+ * class Person {
+ * public String getFirstName();
+ * public String getLastName();
+ * }
+ *
+ *
+ * would be rendered as: ... ...
+ */
+public class BeanAdapter extends AbstractAdapterElement {
+ //~ Static fields/initializers /////////////////////////////////////////////
+
+ private static final Object[] NULLPARAMS = new Object[0];
+
+ /**
+ * Cache can savely be static because the cached information is the same for all instances of this class.
+ */
+ private static Map propertyDescriptorCache;
+
+ //~ Instance fields ////////////////////////////////////////////////////////
+
+ private Log log = LogFactory.getLog(this.getClass());
+
+ //~ Constructors ///////////////////////////////////////////////////////////
+
+ public BeanAdapter() {
+ }
+
+ public BeanAdapter(
+ AdapterFactory adapterFactory, AdapterNode parent, String propertyName, Object value) {
+ setContext(adapterFactory, parent, propertyName, value);
+ }
+
+ //~ Methods ////////////////////////////////////////////////////////////////
+
+ public String getTagName() {
+ return getPropertyName();
+ }
+
+ public NodeList getChildNodes() {
+ NodeList nl = super.getChildNodes();
+ // Log child nodes for debug:
+ if (log.isDebugEnabled() && nl != null) {
+ log.debug("BeanAdapter getChildNodes for: " + getTagName());
+ log.debug(nl.toString());
+ }
+ return nl;
+ }
+
+ protected List buildChildAdapters() {
+ log.debug("BeanAdapter building children. PropName = " + getPropertyName());
+ List newAdapters = new ArrayList();
+ Class type = getPropertyValue().getClass();
+ PropertyDescriptor[] props = getPropertyDescriptors(getPropertyValue());
+
+ if (props.length > 0) {
+ for (PropertyDescriptor prop : props) {
+ Method m = prop.getReadMethod();
+ log.debug("Bean reading property method: " + m.getName());
+
+ if (m == null) {
+ //FIXME: write only property or indexed access
+ continue;
+ }
+
+ String propertyName = prop.getName();
+ Object propertyValue;
+
+ /*
+ Unwrap any invocation target exceptions and log them.
+ We really need a way to control which properties are accessed.
+ Perhaps with annotations in Java5?
+ */
+ try {
+ propertyValue = m.invoke(getPropertyValue(), NULLPARAMS);
+ } catch (Exception e) {
+ if (e instanceof InvocationTargetException)
+ e = (Exception) ((InvocationTargetException) e).getTargetException();
+ log.error(e);
+ continue;
+ }
+
+ Node childAdapter;
+
+ if (propertyValue == null) {
+ childAdapter = getAdapterFactory().adaptNullValue(this, propertyName);
+ } else {
+ childAdapter = getAdapterFactory().adaptNode(this, propertyName, propertyValue);
+ }
+
+ if (childAdapter != null)
+ newAdapters.add(childAdapter);
+
+ if (log.isDebugEnabled()) {
+ log.debug(this + " adding adapter: " + childAdapter);
+ }
+ }
+ } else {
+ // No properties found
+ log.info(
+ "Class " + type.getName() + " has no readable properties, " + " trying to adapt " + getPropertyName() + " with StringAdapter...");
+ }
+
+ return newAdapters;
+ }
+
+ /**
+ * Caching facade method to Introspector.getBeanInfo(Class, Class).getPropertyDescriptors();
+ */
+ private synchronized PropertyDescriptor[] getPropertyDescriptors(Object bean) {
+ try {
+ if (propertyDescriptorCache == null) {
+ propertyDescriptorCache = new HashMap();
+ }
+
+ PropertyDescriptor[] props = propertyDescriptorCache.get(bean.getClass());
+
+ if (props == null) {
+ log.debug("Caching property descriptor for " + bean.getClass().getName());
+ props = Introspector.getBeanInfo(bean.getClass(), Object.class).getPropertyDescriptors();
+ propertyDescriptorCache.put(bean.getClass(), props);
+ }
+
+ return props;
+ } catch (IntrospectionException e) {
+ e.printStackTrace();
+ throw new StrutsException("Error getting property descriptors for " + bean + " : " + e.getMessage());
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/CollectionAdapter.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/CollectionAdapter.java
new file mode 100644
index 000000000..4fa7dc843
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/CollectionAdapter.java
@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.w3c.dom.Node;
+
+
+/**
+ */
+public class CollectionAdapter extends AbstractAdapterElement {
+
+ private Log log = LogFactory.getLog(this.getClass());
+
+ public CollectionAdapter() { }
+
+ public CollectionAdapter(AdapterFactory rootAdapterFactory, AdapterNode parent, String propertyName, Object value) {
+ setContext(rootAdapterFactory, parent, propertyName, value);
+ }
+
+ protected List buildChildAdapters() {
+ Collection values = (Collection) getPropertyValue();
+ List children = new ArrayList(values.size());
+
+ for (Object value : values) {
+ Node childAdapter = getAdapterFactory().adaptNode(this, "item", value);
+ if (childAdapter != null)
+ children.add(childAdapter);
+
+ if (log.isDebugEnabled()) {
+ log.debug(this + " adding adapter: " + childAdapter);
+ }
+ }
+
+ return children;
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/MapAdapter.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/MapAdapter.java
new file mode 100644
index 000000000..5c835940f
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/MapAdapter.java
@@ -0,0 +1,83 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.w3c.dom.Node;
+
+/**
+ * MapAdapter adapters a java.util.Map type to an XML DOM with the following
+ * structure:
+ *
+ *
+ *
+ * ...
+ * ...
+ *
+ * ...
+ *
+ *
+ */
+public class MapAdapter extends AbstractAdapterElement {
+
+ public MapAdapter() { }
+
+ public MapAdapter(AdapterFactory adapterFactory, AdapterNode parent, String propertyName, Map value) {
+ setContext( adapterFactory, parent, propertyName, value );
+ }
+
+ public Map map() {
+ return (Map)getPropertyValue();
+ }
+
+ protected List buildChildAdapters() {
+ List children = new ArrayList(map().entrySet().size());
+
+ for (Object o : map().entrySet()) {
+ Map.Entry entry = (Map.Entry) o;
+ Object key = entry.getKey();
+ Object value = entry.getValue();
+ EntryElement child = new EntryElement(
+ getAdapterFactory(), this, "entry", key, value);
+ children.add(child);
+ }
+
+ return children;
+ }
+
+ class EntryElement extends AbstractAdapterElement {
+ Object key, value;
+
+ public EntryElement( AdapterFactory adapterFactory,
+ AdapterNode parent, String propertyName, Object key, Object value ) {
+ setContext( adapterFactory, parent, propertyName, null/*we have two values*/ );
+ this.key = key;
+ this.value = value;
+ }
+
+ protected List buildChildAdapters() {
+ List children = new ArrayList();
+ children.add( getAdapterFactory().adaptNode( this, "key", key ) );
+ children.add( getAdapterFactory().adaptNode( this, "value", value ) );
+ return children;
+ }
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyAttrAdapter.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyAttrAdapter.java
new file mode 100644
index 000000000..1e0e4c6eb
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyAttrAdapter.java
@@ -0,0 +1,82 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Element;
+import org.w3c.dom.TypeInfo;
+
+/**
+ * ProxyAttrAdapter is a pass-through adapter for objects which already
+ * implement the Attr interface. All methods are proxied to the underlying
+ * Node except node traversal (e.g. getParent()) related methods which
+ * are implemented by the abstract adapter node to work with the parent adapter.
+ */
+public class ProxyAttrAdapter extends ProxyNodeAdapter implements Attr {
+
+ public ProxyAttrAdapter(AdapterFactory factory, AdapterNode parent, Attr value) {
+ super(factory, parent, value);
+ }
+
+ // convenience
+ protected Attr attr() {
+ return (Attr) getPropertyValue();
+ }
+
+ // Proxied Attr methods
+
+ public String getName() {
+ return attr().getName();
+ }
+
+ public boolean getSpecified() {
+ return attr().getSpecified();
+ }
+
+ public String getValue() {
+ return attr().getValue();
+ }
+
+ public void setValue(String string) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Element getOwnerElement() {
+ return (Element) getParent();
+ }
+
+ // DOM level 3
+
+ public TypeInfo getSchemaTypeInfo() {
+ throw operationNotSupported();
+ }
+
+ public boolean isId() {
+ throw operationNotSupported();
+ }
+
+ // end DOM level 3
+
+ // End Proxied Attr methods
+
+ public String toString() {
+ return "ProxyAttribute for: " + attr();
+ }
+}
+
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyElementAdapter.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyElementAdapter.java
new file mode 100644
index 000000000..c3a073f51
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyElementAdapter.java
@@ -0,0 +1,165 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.TypeInfo;
+
+/**
+ * ProxyElementAdapter is a pass-through adapter for objects which already
+ * implement the Element interface. All methods are proxied to the underlying
+ * Node except getParent(), getNextSibling() and getPreviousSibling(), which
+ * are implemented by the abstract adapter node to work with the parent adapter.
+ *
+ * Note: this class wants to be (extend) both an AbstractElementAdapter
+ * and ProxyElementAdapter, but its proxy-ness is winning right now.
+ */
+public class ProxyElementAdapter extends ProxyNodeAdapter implements Element {
+
+ private Log log = LogFactory.getLog(this.getClass());
+
+ public ProxyElementAdapter(AdapterFactory factory, AdapterNode parent, Element value) {
+ super(factory, parent, value);
+ }
+
+ /**
+ * Get the proxied Element
+ */
+ protected Element element() {
+ return (Element) getPropertyValue();
+ }
+
+ protected List buildChildAdapters() {
+ List adapters = new ArrayList();
+ NodeList children = node().getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ Node adapter = wrap(child);
+ if (adapter != null) {
+ log.debug("wrapped child node: " + child.getNodeName());
+ adapters.add(adapter);
+ }
+ }
+ return adapters;
+ }
+
+ // Proxied Element methods
+
+ public String getTagName() {
+ return element().getTagName();
+ }
+
+ public boolean hasAttribute(String name) {
+ return element().hasAttribute(name);
+ }
+
+ public String getAttribute(String name) {
+ return element().getAttribute(name);
+ }
+
+ public boolean hasAttributeNS(String namespaceURI, String localName) {
+ return element().hasAttributeNS(namespaceURI, localName);
+ }
+
+ public Attr getAttributeNode(String name) {
+ log.debug("wrapping attribute");
+ return (Attr) wrap(element().getAttributeNode(name));
+ }
+
+ // I'm overriding this just for clarity. The base impl is correct.
+ public NodeList getElementsByTagName(String name) {
+ return super.getElementsByTagName(name);
+ }
+
+ public String getAttributeNS(String namespaceURI, String localName) {
+ return element().getAttributeNS(namespaceURI, localName);
+ }
+
+ public Attr getAttributeNodeNS(String namespaceURI, String localName) {
+ return (Attr) wrap(element().getAttributeNodeNS(namespaceURI, localName));
+ }
+
+ public NodeList getElementsByTagNameNS(String namespaceURI, String localName) {
+ return super.getElementsByTagNameNS(namespaceURI, localName);
+ }
+
+ // Unsupported mutators of Element
+
+ public void removeAttribute(String name) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void removeAttributeNS(String namespaceURI, String localName) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setAttribute(String name, String value) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Attr removeAttributeNode(Attr oldAttr) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Attr setAttributeNode(Attr newAttr) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Attr setAttributeNodeNS(Attr newAttr) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setAttributeNS(String namespaceURI, String qualifiedName, String value) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ // end proxied Element methods
+
+ // unsupported DOM level 3 methods
+
+ public TypeInfo getSchemaTypeInfo() {
+ throw operationNotSupported();
+ }
+
+ public void setIdAttribute(String string, boolean b) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public void setIdAttributeNS(String string, String string1, boolean b) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public void setIdAttributeNode(Attr attr, boolean b) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ // end DOM level 3 methods
+
+ public String toString() {
+ return "ProxyElement for: " + element();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyNamedNodeMap.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyNamedNodeMap.java
new file mode 100644
index 000000000..fd3166b62
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyNamedNodeMap.java
@@ -0,0 +1,78 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * A NamedNodeMap that wraps the Nodes returned in their proxies.
+ *
+ * Note: Since maps have no guaranteed order we don't need to worry about identity
+ * here as we do with "child" adapters. In that case we need to preserve identity
+ * in order to support finding the next/previous siblings.
+ */
+public class ProxyNamedNodeMap implements NamedNodeMap {
+
+ private NamedNodeMap nodes;
+ private AdapterFactory adapterFactory;
+ private AdapterNode parent;
+
+ public ProxyNamedNodeMap(AdapterFactory factory, AdapterNode parent, NamedNodeMap nodes) {
+ this.nodes = nodes;
+ this.adapterFactory = factory;
+ this.parent = parent;
+ }
+
+ protected Node wrap(Node node) {
+ return adapterFactory.proxyNode(parent, node);
+ }
+
+ public int getLength() {
+ return nodes.getLength();
+ }
+
+ public Node item(int index) {
+ return wrap(nodes.item(index));
+ }
+
+ public Node getNamedItem(String name) {
+ return wrap(nodes.getNamedItem(name));
+ }
+
+ public Node removeNamedItem(String name) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Node setNamedItem(Node arg) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Node setNamedItemNS(Node arg) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Node getNamedItemNS(String namespaceURI, String localName) {
+ return wrap(nodes.getNamedItemNS(namespaceURI, localName));
+ }
+
+ public Node removeNamedItemNS(String namespaceURI, String localName) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyNodeAdapter.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyNodeAdapter.java
new file mode 100644
index 000000000..247c42aab
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyNodeAdapter.java
@@ -0,0 +1,131 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * ProxyNodeAdapter is a read-only delegating adapter for objects which already
+ * implement the Node interface. All methods are proxied to the underlying
+ * Node except getParent(), getNextSibling() and getPreviousSibling(), which
+ * are implemented by the abstract adapter node to work with the parent adapter.
+ */
+public abstract class ProxyNodeAdapter extends AbstractAdapterNode {
+
+ private Log log = LogFactory.getLog(this.getClass());
+
+ public ProxyNodeAdapter(AdapterFactory factory, AdapterNode parent, Node value) {
+ setContext(factory, parent, "document"/*propname unused*/, value);
+ log.debug("proxied node is: " + value);
+ log.debug("node class is: " + value.getClass());
+ log.debug("node type is: " + value.getNodeType());
+ log.debug("node name is: " + value.getNodeName());
+ }
+
+ /**
+ * Get the proxied Node value
+ */
+ protected Node node() {
+ return (Node) getPropertyValue();
+ }
+
+ /**
+ * Get and adapter to wrap the proxied node.
+ *
+ * @param node
+ */
+ protected Node wrap(Node node) {
+ return getAdapterFactory().proxyNode(this, node);
+ }
+
+ protected NamedNodeMap wrap(NamedNodeMap nnm) {
+ return getAdapterFactory().proxyNamedNodeMap(this, nnm);
+ }
+ //protected NodeList wrap( NodeList nl ) { }
+
+ //protected Node unwrap( Node child ) {
+ // return ((ProxyNodeAdapter)child).node();
+ //}
+
+ // Proxied Node methods
+
+ public String getNodeName() {
+ log.trace("getNodeName");
+ return node().getNodeName();
+ }
+
+ public String getNodeValue() throws DOMException {
+ log.trace("getNodeValue");
+ return node().getNodeValue();
+ }
+
+ public short getNodeType() {
+ if (log.isTraceEnabled())
+ log.trace("getNodeType: " + getNodeName() + ": " + node().getNodeType());
+ return node().getNodeType();
+ }
+
+ public NamedNodeMap getAttributes() {
+ NamedNodeMap nnm = wrap(node().getAttributes());
+ if (log.isTraceEnabled())
+ log.trace("getAttributes: " + nnm);
+ return nnm;
+ }
+
+ public boolean hasChildNodes() {
+ log.trace("hasChildNodes");
+ return node().hasChildNodes();
+ }
+
+ public boolean isSupported(String s, String s1) {
+ log.trace("isSupported");
+ // Is this ok? What kind of features are they asking about?
+ return node().isSupported(s, s1);
+ }
+
+ public String getNamespaceURI() {
+ log.trace("getNamespaceURI");
+ return node().getNamespaceURI();
+ }
+
+ public String getPrefix() {
+ log.trace("getPrefix");
+ return node().getPrefix();
+ }
+
+ public String getLocalName() {
+ log.trace("getLocalName");
+ return node().getLocalName();
+ }
+
+ public boolean hasAttributes() {
+ log.trace("hasAttributes");
+ return node().hasAttributes();
+ }
+
+ // End proxied Node methods
+
+ public String toString() {
+ return "ProxyNode for: " + node();
+ }
+}
+
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyTextNodeAdapter.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyTextNodeAdapter.java
new file mode 100644
index 000000000..0fc9cccb8
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ProxyTextNodeAdapter.java
@@ -0,0 +1,94 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Text;
+
+/**
+ * ProxyTextNodeAdapter is a pass-through adapter for objects which already
+ * implement the Text interface. All methods are proxied to the underlying
+ * Node except getParent(), getNextSibling() and getPreviousSibling(), which
+ * are implemented by the abstract adapter node to work with the parent adapter.
+ */
+public class ProxyTextNodeAdapter extends ProxyNodeAdapter implements Text {
+
+ public ProxyTextNodeAdapter(AdapterFactory factory, AdapterNode parent, Text value) {
+ super(factory, parent, value);
+ }
+
+ // convenience
+ Text text() {
+ return (Text) getPropertyValue();
+ }
+
+ public String toString() {
+ return "ProxyTextNode for: " + text();
+ }
+
+ public Text splitText(int offset) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public int getLength() {
+ return text().getLength();
+ }
+
+ public void deleteData(int offset, int count) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getData() throws DOMException {
+ return text().getData();
+ }
+
+ public String substringData(int offset, int count) throws DOMException {
+ return text().substringData(offset, count);
+ }
+
+ public void replaceData(int offset, int count, String arg) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void insertData(int offset, String arg) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void appendData(String arg) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setData(String data) throws DOMException {
+ throw new UnsupportedOperationException();
+ }
+
+ // DOM level 3
+
+ public boolean isElementContentWhitespace() {
+ throw operationNotSupported();
+ }
+
+ public String getWholeText() {
+ throw operationNotSupported();
+ }
+
+ public Text replaceWholeText(String string) throws DOMException {
+ throw operationNotSupported();
+ }
+}
+
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/ServletURIResolver.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ServletURIResolver.java
new file mode 100644
index 000000000..c295d4f74
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/ServletURIResolver.java
@@ -0,0 +1,70 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.io.InputStream;
+
+import javax.servlet.ServletContext;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * ServletURIResolver is a URIResolver that can retrieve resources from the servlet context using the scheme "response".
+ * e.g.
+ *
+ * A URI resolver is called when a stylesheet uses an xsl:include, xsl:import, or document() function to find the
+ * resource (file).
+ */
+public class ServletURIResolver implements URIResolver {
+
+ private Log log = LogFactory.getLog(getClass());
+ static final String PROTOCOL = "response:";
+
+ private ServletContext sc;
+
+ public ServletURIResolver(ServletContext sc) {
+ log.trace("ServletURIResolver: " + sc);
+ this.sc = sc;
+ }
+
+ public Source resolve(String href, String base) throws TransformerException {
+ log.debug("ServletURIResolver resolve(): href=" + href + ", base=" + base);
+ if (href.startsWith(PROTOCOL)) {
+ String res = href.substring(PROTOCOL.length());
+ log.debug("Resolving resource <" + res + ">");
+
+ InputStream is = sc.getResourceAsStream(res);
+
+ if (is == null) {
+ throw new TransformerException(
+ "Resource " + res + " not found in resources.");
+ }
+
+ return new StreamSource(is);
+ }
+
+ throw new TransformerException(
+ "Cannot handle procotol of resource " + href);
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/SimpleAdapterDocument.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/SimpleAdapterDocument.java
new file mode 100644
index 000000000..3879b304e
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/SimpleAdapterDocument.java
@@ -0,0 +1,254 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.struts2.StrutsException;
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+
+/**
+ * SimpleAdapterDocument adapted a Java object and presents it as
+ * a Document. This class represents the Document container and uses
+ * the AdapterFactory to produce a child adapter for the wrapped object.
+ * The adapter produced must be of an Element type or an exception is thrown.
+ *
+ * Note: in theory we could base this on AbstractAdapterElement and then allow
+ * the wrapped object to be a more general Node type. We would just use
+ * ourselves as the root element. However I don't think this is an issue as
+ * people expect Documents to wrap Elements.
+ */
+public class SimpleAdapterDocument extends AbstractAdapterNode implements Document {
+
+ private Element rootElement;
+
+ public SimpleAdapterDocument(
+ AdapterFactory adapterFactory, AdapterNode parent, String propertyName, Object value) {
+ setContext(adapterFactory, parent, propertyName, value);
+
+ }
+
+ public void setPropertyValue(Object prop) {
+ super.setPropertyValue(prop);
+ rootElement = null; // recreate the root element
+ }
+
+ /**
+ * Lazily construct the root element adapter from the value object.
+ */
+ private Element getRootElement() {
+ if (rootElement != null)
+ return rootElement;
+
+ Node node = getAdapterFactory().adaptNode(
+ this, getPropertyName(), getPropertyValue());
+ if (node instanceof Element)
+ rootElement = (Element) node;
+ else
+ throw new StrutsException(
+ "Document adapter expected to wrap an Element type. Node is not an element:" + node);
+
+ return rootElement;
+ }
+
+ protected List getChildAdapters() {
+ return Arrays.asList(new Node[]{getRootElement()});
+ }
+
+ public NodeList getChildNodes() {
+ return new NodeList() {
+ public Node item(int i) {
+ return getRootElement();
+ }
+
+ public int getLength() {
+ return 1;
+ }
+ };
+ }
+
+ public DocumentType getDoctype() {
+ return null;
+ }
+
+ public Element getDocumentElement() {
+ return getRootElement();
+ }
+
+ public Element getElementById(String string) {
+ return null;
+ }
+
+ public NodeList getElementsByTagName(String string) {
+ return null;
+ }
+
+ public NodeList getElementsByTagNameNS(String string, String string1) {
+ return null;
+ }
+
+ public Node getFirstChild() {
+ return getRootElement();
+ }
+
+ public DOMImplementation getImplementation() {
+ return null;
+ }
+
+ public Node getLastChild() {
+ return getRootElement();
+ }
+
+ public String getNodeName() {
+ return "#document";
+ }
+
+ public short getNodeType() {
+ return Node.DOCUMENT_NODE;
+ }
+
+ public Attr createAttribute(String string) throws DOMException {
+ return null;
+ }
+
+ public Attr createAttributeNS(String string, String string1) throws DOMException {
+ return null;
+ }
+
+ public CDATASection createCDATASection(String string) throws DOMException {
+ return null;
+ }
+
+ public Comment createComment(String string) {
+ return null;
+ }
+
+ public DocumentFragment createDocumentFragment() {
+ return null;
+ }
+
+ public Element createElement(String string) throws DOMException {
+ return null;
+ }
+
+ public Element createElementNS(String string, String string1) throws DOMException {
+ return null;
+ }
+
+ public EntityReference createEntityReference(String string) throws DOMException {
+ return null;
+ }
+
+ public ProcessingInstruction createProcessingInstruction(String string, String string1) throws DOMException {
+ return null;
+ }
+
+ public Text createTextNode(String string) {
+ return null;
+ }
+
+ public boolean hasChildNodes() {
+ return true;
+ }
+
+ public Node importNode(Node node, boolean b) throws DOMException {
+ return null;
+ }
+
+ public Node getChildAfter(Node child) {
+ return null;
+ }
+
+ public Node getChildBefore(Node child) {
+ return null;
+ }
+
+ // DOM level 3
+
+ public String getInputEncoding() {
+ throw operationNotSupported();
+ }
+
+ public String getXmlEncoding() {
+ throw operationNotSupported();
+ }
+
+ public boolean getXmlStandalone() {
+ throw operationNotSupported();
+ }
+
+ public void setXmlStandalone(boolean b) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public String getXmlVersion() {
+ throw operationNotSupported();
+ }
+
+ public void setXmlVersion(String string) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public boolean getStrictErrorChecking() {
+ throw operationNotSupported();
+ }
+
+ public void setStrictErrorChecking(boolean b) {
+ throw operationNotSupported();
+ }
+
+ public String getDocumentURI() {
+ throw operationNotSupported();
+ }
+
+ public void setDocumentURI(String string) {
+ throw operationNotSupported();
+ }
+
+ public Node adoptNode(Node node) throws DOMException {
+ throw operationNotSupported();
+ }
+
+ public DOMConfiguration getDomConfig() {
+ throw operationNotSupported();
+ }
+
+ public void normalizeDocument() {
+ throw operationNotSupported();
+ }
+
+ public Node renameNode(Node node, String string, String string1) throws DOMException {
+ return null;
+ }
+ // end DOM level 3
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/SimpleNodeList.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/SimpleNodeList.java
new file mode 100644
index 000000000..ba4993159
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/SimpleNodeList.java
@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class SimpleNodeList implements NodeList {
+
+ private Log log = LogFactory.getLog(SimpleNodeList.class);
+
+ private List nodes;
+
+ public SimpleNodeList(List nodes) {
+ this.nodes = nodes;
+ }
+
+ public int getLength() {
+ if (log.isTraceEnabled())
+ log.trace("getLength: " + nodes.size());
+ return nodes.size();
+ }
+
+ public Node item(int i) {
+ log.trace("getItem: " + i);
+ return nodes.get(i);
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer("SimpleNodeList: [");
+ for (int i = 0; i < getLength(); i++)
+ sb.append(item(i).getNodeName() + ',');
+ sb.append("]");
+ return sb.toString();
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/SimpleTextNode.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/SimpleTextNode.java
new file mode 100644
index 000000000..152d9332b
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/SimpleTextNode.java
@@ -0,0 +1,102 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import org.apache.struts2.StrutsException;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+
+/**
+ *
+ */
+public class SimpleTextNode extends AbstractAdapterNode implements Node, Text {
+
+ public SimpleTextNode(AdapterFactory rootAdapterFactory, AdapterNode parent, String propertyName, Object value) {
+ setContext(rootAdapterFactory, parent, propertyName, value);
+ }
+
+ protected String getStringValue() {
+ return getPropertyValue().toString();
+ }
+
+ public void setData(String string) throws DOMException {
+ throw new StrutsException("Operation not supported");
+ }
+
+ public String getData() throws DOMException {
+ return getStringValue();
+ }
+
+ public int getLength() {
+ return getStringValue().length();
+ }
+
+ public String getNodeName() {
+ return "#text";
+ }
+
+ public short getNodeType() {
+ return Node.TEXT_NODE;
+ }
+
+ public String getNodeValue() throws DOMException {
+ return getStringValue();
+ }
+
+ public void appendData(String string) throws DOMException {
+ throw new StrutsException("Operation not supported");
+ }
+
+ public void deleteData(int i, int i1) throws DOMException {
+ throw new StrutsException("Operation not supported");
+ }
+
+ public void insertData(int i, String string) throws DOMException {
+ throw new StrutsException("Operation not supported");
+ }
+
+ public void replaceData(int i, int i1, String string) throws DOMException {
+ throw new StrutsException("Operation not supported");
+ }
+
+ public Text splitText(int i) throws DOMException {
+ throw new StrutsException("Operation not supported");
+ }
+
+ public String substringData(int beginIndex, int endIndex) throws DOMException {
+ return getStringValue().substring(beginIndex, endIndex);
+ }
+
+ // DOM level 3
+
+ public boolean isElementContentWhitespace() {
+ throw operationNotSupported();
+ }
+
+ public String getWholeText() {
+ throw operationNotSupported();
+ }
+
+ public Text replaceWholeText(String string) throws DOMException {
+ throw operationNotSupported();
+ }
+ // end DOM level 3
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/StringAdapter.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/StringAdapter.java
new file mode 100644
index 000000000..fe3a5eb52
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/StringAdapter.java
@@ -0,0 +1,112 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.w3c.dom.Node;
+
+import org.xml.sax.InputSource;
+
+import com.opensymphony.xwork2.util.DomHelper;
+
+/**
+ * StringAdapter adapts a Java String value to a DOM Element with the specified
+ * property name containing the String's text.
+ * e.g. a property String getFoo() { return "My Text!"; }
+ * will appear in the result DOM as:
+ * MyText!
+ *
+ * Subclasses may override the getStringValue() method in order to use StringAdapter
+ * as a simplified custom XML adapter for Java types. A subclass can enable XML
+ * parsing of the value string via the setParseStringAsXML() method and then
+ * override getStringValue() to return a String containing the custom formatted XML.
+ *
+ */
+public class StringAdapter extends AbstractAdapterElement {
+
+ private Log log = LogFactory.getLog(this.getClass());
+ boolean parseStringAsXML;
+
+ public StringAdapter() {
+ }
+
+ public StringAdapter(AdapterFactory adapterFactory, AdapterNode parent, String propertyName, String value) {
+ setContext(adapterFactory, parent, propertyName, value);
+ }
+
+ /**
+ * Get the object to be adapted as a String value.
+ *
+ * This method can be overridden by subclasses that wish to use StringAdapter
+ * as a simplified customizable XML adapter for Java types. A subclass can
+ * enable parsing of the value string as containing XML text via the
+ * setParseStringAsXML() method and then override getStringValue() to return a
+ * String containing the custom formatted XML.
+ */
+ protected String getStringValue() {
+ return getPropertyValue().toString();
+ }
+
+ protected List buildChildAdapters() {
+ Node node;
+ if (getParseStringAsXML()) {
+ log.debug("parsing string as xml: " + getStringValue());
+ // Parse the String to a DOM, then proxy that as our child
+ node = DomHelper.parse(new InputSource(new StringReader(getStringValue())));
+ node = getAdapterFactory().proxyNode(this, node);
+ } else {
+ log.debug("using string as is: " + getStringValue());
+ // Create a Text node as our child
+ node = new SimpleTextNode(getAdapterFactory(), this, "text", getStringValue());
+ }
+
+ List children = new ArrayList();
+ children.add(node);
+ return children;
+ }
+
+ /**
+ * Is this StringAdapter to interpret its string values as containing
+ * XML Text?
+ *
+ * @see #setParseStringAsXML(boolean)
+ */
+ public boolean getParseStringAsXML() {
+ return parseStringAsXML;
+ }
+
+ /**
+ * When set to true the StringAdapter will interpret its String value
+ * as containing XML text and parse it to a DOM Element. The new DOM
+ * Element will be a child of this String element. (i.e. wrapped in an
+ * element of the property name specified for this StringAdapter).
+ *
+ * @param parseStringAsXML
+ * @see #getParseStringAsXML()
+ */
+ public void setParseStringAsXML(boolean parseStringAsXML) {
+ this.parseStringAsXML = parseStringAsXML;
+ }
+
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/XSLTResult.java b/trunk/core/src/main/java/org/apache/struts2/views/xslt/XSLTResult.java
new file mode 100644
index 000000000..b0ee149cb
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/XSLTResult.java
@@ -0,0 +1,347 @@
+/*
+ * $Id$
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.views.xslt;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.config.Settings;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.Result;
+import com.opensymphony.xwork2.util.TextParseUtil;
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ *
+ *
+ * XSLTResult uses XSLT to transform action object to XML. Recent version has
+ * been specifically modified to deal with Xalan flaws. When using Xalan you may
+ * notice that even though you have very minimal stylesheet like this one
+ *
+ * <xsl:template match="/result">
+ * <result />
+ * </xsl:template>
+ *
+ *
+ * then Xalan would still iterate through every property of your action and it's
+ * all descendants.
+ *
+ *
+ *
+ * If you had double-linked objects then Xalan would work forever analysing
+ * infinite object tree. Even if your stylesheet was not constructed to process
+ * them all. It's becouse current Xalan eagerly and extensively converts
+ * everything to it's internal DTM model before further processing.
+ *
+ *
+ *
+ * Thet's why there's a loop eliminator added that works by indexing every
+ * object-property combination during processing. If it notices that some
+ * object's property were already walked through, it doesn't get any deeper.
+ * Say, you have two objects x and y with the following properties set
+ * (pseudocode):
+ *
+ *
+ * x.y = y;
+ * and
+ * y.x = x;
+ * action.x=x;
+ *
+ *
+ * Due to that modification the resulting XML document based on x would be:
+ *
+ *
+ *
+ * <result>
+ * <x>
+ * <y/>
+ * </x>
+ * </result>
+ *
+ *
+ * Without it there would be an endless x/y/x/y/x/y/... elements.
+ *
+ *
+ *
+ * The XSLTResult code tries also to deal with the fact that DTM model is built
+ * in a manner that childs are processed before siblings. The result is that if
+ * there is object x that is both set in action's x property, and very deeply
+ * under action's a property then it would only appear under a, not under x.
+ * That's not what we expect, and that's why XSLTResult allows objects to repeat
+ * in various places to some extent.
+ *
+ *
+ *
+ * Sometimes the object mesh is still very dense and you may notice that even
+ * though you have relatively simple stylesheet execution takes a tremendous
+ * amount of time. To help you to deal with that obstacle of Xalan you may
+ * attach regexp filters to elements paths (xpath).
+ *
+ *
+ *
+ * Note: In your .xsl file the root match must be named result .
+ * This example will output the username by using getUsername on your
+ * action class:
+ *
+ * <xsl:template match="result">
+ * <html>
+ * <body>
+ * Hello <xsl:value-of select="username"/> how are you?
+ * </body>
+ * <html>
+ * <xsl:template/>
+ *
+ *
+ *
+ * In the following example the XSLT result would only walk through action's
+ * properties without their childs. It would also skip every property that has
+ * "hugeCollection" in their name. Element's path is first compared to
+ * excludingPattern - if it matches it's no longer processed. Then it is
+ * compared to matchingPattern and processed only if there's a match.
+ *
+ *
+ *
+ *
+ *
+ * <result name="success" type="xslt">
+ * <param name="location">foo.xslt</param>
+ * <param name="matchingPattern">^/result/[^/*]$</param>
+ * <param name="excludingPattern">.*(hugeCollection).*</param>
+ * </result>
+ *
+ *
+ * This result type takes the following parameters:
+ *
+ *
+ *
+ *
+ *
+ * location (default) - the location to go to after execution.
+ *
+ * parse - true by default. If set to false, the location param will
+ * not be parsed for Ognl expressions.
+ *
+ * matchingPattern - Pattern that matches only desired elements, by
+ * default it matches everything.
+ *
+ * excludingPattern - Pattern that eliminates unwanted elements, by
+ * default it matches none.
+ *
+ *
+ *
+ *
+ * struts.properties related configuration:
+ *
+ *
+ *
+ * struts.xslt.nocache - Defaults to false. If set to true, disables
+ * stylesheet caching. Good for development, bad for production.
+ *
+ *
+ *
+ *
+ *
+ * Example:
+ *
+ *
+ * <result name="success" type="xslt">foo.xslt</result>
+ *
+ *
+ */
+public class XSLTResult implements Result {
+
+ private static final long serialVersionUID = 6424691441777176763L;
+ private static final Log log = LogFactory.getLog(XSLTResult.class);
+ public static final String DEFAULT_PARAM = "stylesheetLocation";
+
+ protected boolean noCache;
+ private final Map templatesCache;
+ private String stylesheetLocation;
+ private boolean parse;
+ private AdapterFactory adapterFactory;
+
+ public XSLTResult() {
+ templatesCache = new HashMap();
+ noCache = Settings.get("struts.xslt.nocache").trim().equalsIgnoreCase("true");
+ }
+
+ public XSLTResult(String stylesheetLocation) {
+ this();
+ setStylesheetLocation(stylesheetLocation);
+ }
+
+ /**
+ * @deprecated Use #setStylesheetLocation(String)
+ */
+ public void setLocation(String location) {
+ setStylesheetLocation(location);
+ }
+
+ public void setStylesheetLocation(String location) {
+ if (location == null)
+ throw new IllegalArgumentException("Null location");
+ this.stylesheetLocation = location;
+ }
+
+ public String getStylesheetLocation() {
+ return stylesheetLocation;
+ }
+
+ /**
+ * If true, parse the stylesheet location for OGNL expressions.
+ *
+ * @param parse
+ */
+ public void setParse(boolean parse) {
+ this.parse = parse;
+ }
+
+ public void execute(ActionInvocation invocation) throws Exception {
+ long startTime = System.currentTimeMillis();
+ String location = getStylesheetLocation();
+
+ if (parse) {
+ ValueStack stack = ActionContext.getContext().getValueStack();
+ location = TextParseUtil.translateVariables(location, stack);
+ }
+
+ try {
+ HttpServletResponse response = ServletActionContext.getResponse();
+
+ Writer writer = response.getWriter();
+
+ // Create a transformer for the stylesheet.
+ Templates templates = null;
+ Transformer transformer;
+ if (location != null) {
+ templates = getTemplates(location);
+ transformer = templates.newTransformer();
+ } else
+ transformer = TransformerFactory.newInstance().newTransformer();
+
+ transformer.setURIResolver(getURIResolver());
+
+ String mimeType;
+ if (templates == null)
+ mimeType = "text/xml"; // no stylesheet, raw xml
+ else
+ mimeType = templates.getOutputProperties().getProperty(OutputKeys.MEDIA_TYPE);
+ if (mimeType == null) {
+ // guess (this is a servlet, so text/html might be the best guess)
+ mimeType = "text/html";
+ }
+
+ response.setContentType(mimeType);
+
+ Source xmlSource = getDOMSourceForStack(invocation.getAction());
+
+ // Transform the source XML to System.out.
+ PrintWriter out = response.getWriter();
+
+ log.debug("xmlSource = " + xmlSource);
+ transformer.transform(xmlSource, new StreamResult(out));
+
+ out.close(); // ...and flush...
+
+ if (log.isDebugEnabled()) {
+ log.debug("Time:" + (System.currentTimeMillis() - startTime) + "ms");
+ }
+
+ writer.flush();
+ } catch (Exception e) {
+ log.error("Unable to render XSLT Template, '" + location + "'", e);
+ throw e;
+ }
+ }
+
+ protected AdapterFactory getAdapterFactory() {
+ if (adapterFactory == null)
+ adapterFactory = new AdapterFactory();
+ return adapterFactory;
+ }
+
+ protected void setAdapterFactory(AdapterFactory adapterFactory) {
+ this.adapterFactory = adapterFactory;
+ }
+
+ /**
+ * Get the URI Resolver to be called by the processor when it encounters an xsl:include, xsl:import, or document()
+ * function. The default is an instance of ServletURIResolver, which operates relative to the servlet context.
+ */
+ protected URIResolver getURIResolver() {
+ return new ServletURIResolver(
+ ServletActionContext.getServletContext());
+ }
+
+ protected Templates getTemplates(String path) throws TransformerException, IOException {
+ String pathFromRequest = ServletActionContext.getRequest().getParameter("xslt.location");
+
+ if (pathFromRequest != null)
+ path = pathFromRequest;
+
+ if (path == null)
+ throw new TransformerException("Stylesheet path is null");
+
+ Templates templates = templatesCache.get(path);
+
+ if (noCache || (templates == null)) {
+ synchronized (templatesCache) {
+ URL resource = ServletActionContext.getServletContext().getResource(path);
+
+ if (resource == null) {
+ throw new TransformerException("Stylesheet " + path + " not found in resources.");
+ }
+
+ log.debug("Preparing XSLT stylesheet templates: " + path);
+
+ TransformerFactory factory = TransformerFactory.newInstance();
+ templates = factory.newTemplates(new StreamSource(resource.openStream()));
+ templatesCache.put(path, templates);
+ }
+ }
+
+ return templates;
+ }
+
+ protected Source getDOMSourceForStack(Object action)
+ throws IllegalAccessException, InstantiationException {
+ return new DOMSource(getAdapterFactory().adaptDocument("result", action) );
+ }
+}
diff --git a/trunk/core/src/main/java/org/apache/struts2/views/xslt/package.html b/trunk/core/src/main/java/org/apache/struts2/views/xslt/package.html
new file mode 100644
index 000000000..ed05a0a58
--- /dev/null
+++ b/trunk/core/src/main/java/org/apache/struts2/views/xslt/package.html
@@ -0,0 +1,24 @@
+
+
+The new xslt view supports an extensible Java XML adapter framework that makes
+it easy to customize the XML rendering of objects and to incorporate structured
+XML text and arbitarary DOM fragments into the output.
+
+
+The XSLTResult class now uses an extensible adapter factory for rendering the
+Struts action Java object tree to an XML DOM for consumption by the
+stylesheet. Users can easily register their own adapters to produce custom XML
+views of Java types or simply extend a default "String" adapter and return
+plain or XML text to be incorporated into the DOM. The new adapter mechanism
+is capable of proxying existing DOM trees and incorporating them into the
+results, so you can freely mix DOMs produced from other sources into your
+result tree.
+
+
+A default java.util.Map adapter is also now provided to render Maps to XML.
+
+
+Please see the javadoc on the AdapterFactory for more details.
+
+
+
diff --git a/trunk/core/src/main/resources/META-INF/struts-tags.tld b/trunk/core/src/main/resources/META-INF/struts-tags.tld
new file mode 100644
index 000000000..1a515f132
--- /dev/null
+++ b/trunk/core/src/main/resources/META-INF/struts-tags.tld
@@ -0,0 +1,11308 @@
+
+
+
+
+
+ 2.2.3
+ 1.2
+ s
+
+ /struts-tags
+
+ Struts Tags
+
+
+
+ head
+ org.apache.struts2.views.jsp.ui.HeadTag
+ empty
+
+
+
+ calendarcss
+ false
+ true
+
+
+
+
+
+ debug
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ push
+ org.apache.struts2.views.jsp.PushTag
+ JSP
+
+
+
+ value
+ true
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ table
+ org.apache.struts2.views.jsp.ui.table.WebTableTag
+ JSP
+
+
+
+ modelName
+ true
+ true
+
+
+
+
+
+ sortColumn
+ false
+ true
+
+
+
+
+
+ sortOrder
+ false
+ true
+
+
+
+
+
+ sortable
+ false
+ true
+
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ component
+ org.apache.struts2.views.jsp.ui.ComponentTag
+ JSP
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ token
+ org.apache.struts2.views.jsp.ui.TokenTag
+ JSP
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ set
+ org.apache.struts2.views.jsp.SetTag
+ empty
+
+
+
+ name
+ true
+ true
+
+
+ value]]>
+
+
+
+ scope
+ false
+ true
+
+
+ application, session , request , page , or action .]]>
+
+
+
+ value
+ false
+ true
+
+ name]]>
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ i18n
+ org.apache.struts2.views.jsp.I18nTag
+ JSP
+
+
+
+ name
+ true
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ merge
+ org.apache.struts2.views.jsp.iterator.MergeIteratorTag
+ JSP
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ password
+ org.apache.struts2.views.jsp.ui.PasswordTag
+ JSP
+
+
+
+ showPassword
+ false
+ true
+
+
+
+
+
+ maxlength
+ false
+ true
+
+
+
+
+
+ maxLength
+ false
+ true
+
+
+
+
+
+ readonly
+ false
+ true
+
+
+
+
+
+ size
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ submit
+ org.apache.struts2.views.jsp.ui.SubmitTag
+ JSP
+
+
+
+ resultDivId
+ false
+ true
+
+
+
+
+
+
+ onLoadJS
+ false
+ true
+
+
+
+
+
+
+ notifyTopics
+ false
+ true
+
+
+
+
+
+ listenTopics
+ false
+ true
+
+
+
+
+
+ preInvokeJS
+ false
+ true
+
+
+
+
+
+
+ label
+ false
+ true
+
+
+ input type submit, since button text will always be the value parameter. For the type image , alt parameter will be set to this value.]]>
+
+
+
+ src
+ false
+ true
+
+
+ image type submit button. Will have no effect for types input and button .]]>
+
+
+
+ action
+ false
+ true
+
+
+
+
+
+ method
+ false
+ true
+
+
+
+
+
+ align
+ false
+ true
+
+
+
+
+
+ type
+ false
+ true
+
+
+ input, button and image .]]>
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ form
+ org.apache.struts2.views.jsp.ui.FormTag
+ JSP
+
+
+
+ onsubmit
+ false
+ true
+
+
+
+
+
+ action
+ false
+ true
+
+
+
+
+
+ target
+ false
+ true
+
+
+
+
+
+ enctype
+ false
+ true
+
+
+
+
+
+ method
+ false
+ true
+
+
+
+
+
+ namespace
+ false
+ true
+
+
+
+
+
+ validate
+ false
+ true
+
+
+
+
+
+
+ portletMode
+ false
+ true
+
+
+
+
+
+ windowState
+ false
+ true
+
+
+
+
+
+ acceptcharset
+ false
+ true
+
+
+
+
+
+
+ openTemplate
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ include
+ org.apache.struts2.views.jsp.IncludeTag
+ JSP
+
+
+
+ value
+ true
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ div
+ org.apache.struts2.views.jsp.ui.DivTag
+ JSP
+
+
+
+ updateFreq
+ false
+ true
+
+
+
+
+
+ delay
+ false
+ true
+
+
+
+
+
+ loadingText
+ false
+ true
+
+
+
+
+
+
+ listenTopics
+ false
+ true
+
+
+
+
+
+
+ theme
+ false
+ true
+
+
+ This tag will usually use the ajax theme.]]>
+
+
+
+ href
+ false
+ true
+
+
+
+
+
+ errorText
+ false
+ true
+
+
+
+
+
+
+ showErrorTransportText
+ false
+ true
+
+
+
+
+
+ afterLoading
+ false
+ true
+
+
+
+
+
+
+ openTemplate
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ label
+ org.apache.struts2.views.jsp.ui.LabelTag
+ JSP
+
+
+
+ for
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ action
+ org.apache.struts2.views.jsp.ActionTag
+ JSP
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+ name
+ true
+ true
+
+
+
+
+
+
+ namespace
+ false
+ true
+
+
+
+
+
+ executeResult
+ false
+ true
+
+
+
+
+
+
+ ignoreContextParams
+ false
+ true
+
+
+
+
+
+
+
+
+
+ bean
+ org.apache.struts2.views.jsp.BeanTag
+ JSP
+
+
+
+ name
+ true
+ true
+
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ sort
+ org.apache.struts2.views.jsp.iterator.SortIteratorTag
+ JSP
+
+
+
+ comparator
+ true
+ true
+
+
+
+
+
+ source
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+ optgroup
+ org.apache.struts2.views.jsp.ui.OptGroupTag
+ JSP
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ list
+ false
+ true
+
+
+
+
+
+ listKey
+ false
+ true
+
+
+
+
+
+ listValue
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ hidden
+ org.apache.struts2.views.jsp.ui.HiddenTag
+ JSP
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ iterator
+ org.apache.struts2.views.jsp.IteratorTag
+ JSP
+
+
+
+ status
+ false
+ true
+
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ actionerror
+ org.apache.struts2.views.jsp.ui.ActionErrorTag
+ empty
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ if
+ org.apache.struts2.views.jsp.IfTag
+ JSP
+
+
+
+ test
+ true
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ select
+ org.apache.struts2.views.jsp.ui.SelectTag
+ JSP
+
+
+
+ emptyOption
+ false
+ true
+
+
+
+
+
+ headerKey
+ false
+ true
+
+
+
+
+
+
+ headerValue
+ false
+ true
+
+
+
+
+
+ multiple
+ false
+ true
+
+
+
+
+
+
+ size
+ false
+ true
+
+
+
+
+
+ list
+ true
+ true
+
+
+
+
+
+
+ listKey
+ false
+ true
+
+
+
+
+
+ listValue
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ reset
+ org.apache.struts2.views.jsp.ui.ResetTag
+ JSP
+
+
+
+ label
+ false
+ true
+
+
+ input type reset, since button text will always be the value parameter.]]>
+
+
+
+ action
+ false
+ true
+
+
+
+
+
+ method
+ false
+ true
+
+
+
+
+
+ align
+ false
+ true
+
+
+
+
+
+ type
+ false
+ true
+
+
+ input, button and image .]]>
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ append
+ org.apache.struts2.views.jsp.iterator.AppendIteratorTag
+ JSP
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ updownselect
+ org.apache.struts2.views.jsp.ui.UpDownSelectTag
+ JSP
+
+
+
+ allowMoveUp
+ false
+ true
+
+
+
+
+
+ allowMoveDown
+ false
+ true
+
+
+
+
+
+ allowSelectAll
+ false
+ true
+
+
+
+
+
+ moveUpLabel
+ false
+ true
+
+
+
+
+
+ moveDownLabel
+ false
+ true
+
+
+
+
+
+ selectAllLabel
+ false
+ true
+
+
+
+
+
+ emptyOption
+ false
+ true
+
+
+
+
+
+ headerKey
+ false
+ true
+
+
+
+
+
+
+ headerValue
+ false
+ true
+
+
+
+
+
+ multiple
+ false
+ true
+
+
+
+
+
+
+ size
+ false
+ true
+
+
+
+
+
+ list
+ true
+ true
+
+
+
+
+
+
+ listKey
+ false
+ true
+
+
+
+
+
+ listValue
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ else
+ org.apache.struts2.views.jsp.ElseTag
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ debug
+ org.apache.struts2.views.jsp.ui.DebugTag
+ JSP
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ param
+ org.apache.struts2.views.jsp.ParamTag
+ JSP
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ optiontransferselect
+ org.apache.struts2.views.jsp.ui.OptionTransferSelectTag
+ JSP
+
+
+
+ addAllToLeftLabel
+ false
+ true
+
+
+
+
+
+ addAllToRightLabel
+ false
+ true
+
+
+
+
+
+ addToLeftLabel
+ false
+ true
+
+
+
+
+
+ addToRightLabel
+ false
+ true
+
+
+
+
+
+ allowAddAllToLeft
+ false
+ true
+
+
+
+
+
+ allowAddAllToRight
+ false
+ true
+
+
+
+
+
+ allowAddToLeft
+ false
+ true
+
+
+
+
+
+ allowAddToRight
+ false
+ true
+
+
+
+
+
+ leftTitle
+ false
+ true
+
+
+
+
+
+ rightTitle
+ false
+ true
+
+
+
+
+
+ allowSelectAll
+ false
+ true
+
+
+
+
+
+ selectAllLabel
+ false
+ true
+
+
+
+
+
+ buttonCssClass
+ false
+ true
+
+
+
+
+
+ buttonCssStyle
+ false
+ true
+
+
+
+
+
+ doubleList
+ true
+ true
+
+
+
+
+
+ doubleListKey
+ false
+ true
+
+
+
+
+
+ doubleListValue
+ false
+ true
+
+
+
+
+
+ doubleName
+ true
+ true
+
+
+
+
+
+ doubleValue
+ false
+ true
+
+
+
+
+
+ formName
+ false
+ true
+
+
+
+
+
+ doubleCssClass
+ false
+ true
+
+
+
+
+
+ doubleCssStyle
+ false
+ true
+
+
+
+
+
+ doubleHeaderKey
+ false
+ true
+
+
+
+
+
+ doubleHeaderValue
+ false
+ true
+
+
+
+
+
+ doubleEmptyOption
+ false
+ true
+
+
+
+
+
+ doubleDisabled
+ false
+ true
+
+
+
+
+
+ doubleId
+ false
+ true
+
+
+
+
+
+ doubleMultiple
+ false
+ true
+
+
+
+
+
+ doubleOnblur
+ false
+ true
+
+
+
+
+
+ doubleOnchange
+ false
+ true
+
+
+
+
+
+ doubleOnclick
+ false
+ true
+
+
+
+
+
+ doubleOndblclick
+ false
+ true
+
+
+
+
+
+ doubleOnfocus
+ false
+ true
+
+
+
+
+
+ doubleOnkeydown
+ false
+ true
+
+
+
+
+
+ doubleOnkeypress
+ false
+ true
+
+
+
+
+
+ doubleOnkeyup
+ false
+ true
+
+
+
+
+
+ doubleOnmousedown
+ false
+ true
+
+
+
+
+
+ doubleOnmousemove
+ false
+ true
+
+
+
+
+
+ doubleOnmouseout
+ false
+ true
+
+
+
+
+
+ doubleOnmouseover
+ false
+ true
+
+
+
+
+
+ doubleOnmouseup
+ false
+ true
+
+
+
+
+
+ doubleOnselect
+ false
+ true
+
+
+
+
+
+ doubleSize
+ false
+ true
+
+
+
+
+
+ doubleListKey
+ false
+ true
+
+
+
+
+
+ emptyOption
+ false
+ true
+
+
+
+
+
+ headerKey
+ false
+ true
+
+
+
+
+
+
+ headerValue
+ false
+ true
+
+
+
+
+
+ multiple
+ false
+ true
+
+
+
+
+
+
+ size
+ false
+ true
+
+
+
+
+
+ list
+ true
+ true
+
+
+
+
+
+
+ listKey
+ false
+ true
+
+
+
+
+
+ listValue
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ textfield
+ org.apache.struts2.views.jsp.ui.TextFieldTag
+ JSP
+
+
+
+ maxlength
+ false
+ true
+
+
+
+
+
+ maxLength
+ false
+ true
+
+
+
+
+
+ readonly
+ false
+ true
+
+
+
+
+
+ size
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ doubleselect
+ org.apache.struts2.views.jsp.ui.DoubleSelectTag
+ JSP
+
+
+
+
+ doubleList
+ true
+ true
+
+
+
+
+
+ doubleListKey
+ false
+ true
+
+
+
+
+
+ doubleListValue
+ false
+ true
+
+
+
+
+
+ doubleName
+ true
+ true
+
+
+
+
+
+ doubleValue
+ false
+ true
+
+
+
+
+
+ formName
+ false
+ true
+
+
+
+
+
+ doubleCssClass
+ false
+ true
+
+
+
+
+
+ doubleCssStyle
+ false
+ true
+
+
+
+
+
+ doubleHeaderKey
+ false
+ true
+
+
+
+
+
+ doubleHeaderValue
+ false
+ true
+
+
+
+
+
+ doubleEmptyOption
+ false
+ true
+
+
+
+
+
+ doubleDisabled
+ false
+ true
+
+
+
+
+
+ doubleId
+ false
+ true
+
+
+
+
+
+ doubleMultiple
+ false
+ true
+
+
+
+
+
+ doubleOnblur
+ false
+ true
+
+
+
+
+
+ doubleOnchange
+ false
+ true
+
+
+
+
+
+ doubleOnclick
+ false
+ true
+
+
+
+
+
+ doubleOndblclick
+ false
+ true
+
+
+
+
+
+ doubleOnfocus
+ false
+ true
+
+
+
+
+
+ doubleOnkeydown
+ false
+ true
+
+
+
+
+
+ doubleOnkeypress
+ false
+ true
+
+
+
+
+
+ doubleOnkeyup
+ false
+ true
+
+
+
+
+
+ doubleOnmousedown
+ false
+ true
+
+
+
+
+
+ doubleOnmousemove
+ false
+ true
+
+
+
+
+
+ doubleOnmouseout
+ false
+ true
+
+
+
+
+
+ doubleOnmouseover
+ false
+ true
+
+
+
+
+
+ doubleOnmouseup
+ false
+ true
+
+
+
+
+
+ doubleOnselect
+ false
+ true
+
+
+
+
+
+ doubleSize
+ false
+ true
+
+
+
+
+
+ doubleListKey
+ false
+ true
+
+
+
+
+
+ emptyOption
+ false
+ true
+
+
+
+
+
+ headerKey
+ false
+ true
+
+
+
+
+
+
+ headerValue
+ false
+ true
+
+
+
+
+
+ multiple
+ false
+ true
+
+
+
+
+
+
+ size
+ false
+ true
+
+
+
+
+
+ doubleAccesskey
+ false
+ true
+
+
+
+
+
+ list
+ true
+ true
+
+
+
+
+
+
+ listKey
+ false
+ true
+
+
+
+
+
+ listValue
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ textarea
+ org.apache.struts2.views.jsp.ui.TextareaTag
+ JSP
+
+
+
+ cols
+ false
+ true
+
+
+
+
+
+ readonly
+ false
+ true
+
+
+
+
+
+ rows
+ false
+ true
+
+
+
+
+
+ wrap
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ generator
+ org.apache.struts2.views.jsp.iterator.IteratorGeneratorTag
+ JSP
+
+
+
+ count
+ false
+ true
+
+
+
+
+
+ separator
+ true
+ true
+
+
+ val into entries of the iterator]]>
+
+
+
+ val
+ true
+ true
+
+
+
+
+
+ converter
+ false
+ true
+
+
+ val into an object]]>
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ checkbox
+ org.apache.struts2.views.jsp.ui.CheckboxTag
+ JSP
+
+
+
+ fieldValue
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ date
+ org.apache.struts2.views.jsp.DateTag
+ empty
+
+
+
+ format
+ false
+ false
+
+
+
+
+
+ nice
+ false
+ true
+
+
+
+
+
+ name
+ true
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ a
+ org.apache.struts2.views.jsp.ui.AnchorTag
+ JSP
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+ notifyTopics
+ false
+ true
+
+
+
+
+
+ preInvokeJS
+ false
+ true
+
+
+
+
+
+
+ theme
+ false
+ true
+
+
+ This tag will usually use the ajax theme.]]>
+
+
+
+ href
+ false
+ true
+
+
+
+
+
+ errorText
+ false
+ true
+
+
+
+
+
+
+ showErrorTransportText
+ false
+ true
+
+
+
+
+
+ afterLoading
+ false
+ true
+
+
+
+
+
+
+ openTemplate
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+
+
+
+ file
+ org.apache.struts2.views.jsp.ui.FileTag
+ JSP
+
+
+
+ accept
+ false
+ true
+
+
+
+
+
+ size
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ url
+ org.apache.struts2.views.jsp.URLTag
+ JSP
+
+
+
+ includeParams
+ false
+ true
+
+
+
+
+
+
+ scheme
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ action
+ false
+ true
+
+
+
+
+
+ namespace
+ false
+ true
+
+
+
+
+
+ method
+ false
+ true
+
+
+
+
+
+ encode
+ false
+ true
+
+
+
+
+
+ includeContext
+ false
+ true
+
+
+
+
+
+ portletMode
+ false
+ true
+
+
+
+
+
+ windowState
+ false
+ true
+
+
+
+
+
+ portletUrlType
+ false
+ true
+
+
+
+
+
+ anchor
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ radio
+ org.apache.struts2.views.jsp.ui.RadioTag
+ JSP
+
+
+
+ list
+ true
+ true
+
+
+
+
+
+
+ listKey
+ false
+ true
+
+
+
+
+
+ listValue
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ combobox
+ org.apache.struts2.views.jsp.ui.ComboBoxTag
+ JSP
+
+
+
+ list
+ true
+ true
+
+
+
+
+
+
+ maxlength
+ false
+ true
+
+
+
+
+
+ maxLength
+ false
+ true
+
+
+
+
+
+ readonly
+ false
+ true
+
+
+
+
+
+ size
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ checkboxlist
+ org.apache.struts2.views.jsp.ui.CheckboxListTag
+ JSP
+
+
+
+ list
+ true
+ true
+
+
+
+
+
+
+ listKey
+ false
+ true
+
+
+
+
+
+ listValue
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ panel
+ org.apache.struts2.views.jsp.ui.PanelTag
+ JSP
+
+
+
+ tabName
+ true
+ true
+
+
+
+
+
+ subscribeTopicName
+ false
+ true
+
+
+
+
+
+ remote
+ false
+ true
+
+
+
+
+
+
+ updateFreq
+ false
+ true
+
+
+
+
+
+ delay
+ false
+ true
+
+
+
+
+
+ loadingText
+ false
+ true
+
+
+
+
+
+
+ listenTopics
+ false
+ true
+
+
+
+
+
+
+ theme
+ false
+ true
+
+
+ This tag will usually use the ajax theme.]]>
+
+
+
+ href
+ false
+ true
+
+
+
+
+
+ errorText
+ false
+ true
+
+
+
+
+
+
+ showErrorTransportText
+ false
+ true
+
+
+
+
+
+ afterLoading
+ false
+ true
+
+
+
+
+
+
+ openTemplate
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ actionmessage
+ org.apache.struts2.views.jsp.ui.ActionMessageTag
+ empty
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ tree
+ org.apache.struts2.views.jsp.ui.TreeTag
+ JSP
+
+
+
+ toggle
+ false
+ true
+
+
+
+
+
+ treeSelectedTopic
+ false
+ true
+
+
+
+
+
+ treeExpandedTopic
+ false
+ true
+
+
+
+
+
+ treeCollapsedTopic
+ false
+ true
+
+
+
+
+
+ rootNode
+ false
+ true
+
+
+
+
+
+ childCollectionProperty
+ false
+ true
+
+
+
+
+
+ nodeTitleProperty
+ false
+ true
+
+
+
+
+
+ nodeIdProperty
+ false
+ true
+
+
+
+
+
+ showRootGrid
+ false
+ true
+
+
+
+
+
+ blankIconSrc
+ false
+ true
+
+
+
+
+
+ expandIconSrcMinus
+ false
+ true
+
+
+
+
+
+ expandIconSrcPlus
+ false
+ true
+
+
+
+
+
+ gridIconSrcC
+ false
+ true
+
+
+
+
+
+ gridIconSrcL
+ false
+ true
+
+
+
+
+
+ gridIconSrcP
+ false
+ true
+
+
+
+
+
+ gridIconSrcV
+ false
+ true
+
+
+
+
+
+ gridIconSrcX
+ false
+ true
+
+
+
+
+
+ gridIconSrcY
+ false
+ true
+
+
+
+
+
+ iconHeight
+ false
+ true
+
+
+
+
+
+ iconWidth
+ false
+ true
+
+
+
+
+
+ templateCssPath
+ false
+ true
+
+
+
+
+
+ toggleDuration
+ false
+ true
+
+
+
+
+
+ showGrid
+ false
+ true
+
+
+
+
+
+ openTemplate
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ property
+ org.apache.struts2.views.jsp.PropertyTag
+ empty
+
+
+
+ default
+ false
+ true
+
+ value attribute is null]]>
+
+
+
+ escape
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ tabbedPanel
+ org.apache.struts2.views.jsp.ui.TabbedPanelTag
+ JSP
+
+
+
+ id
+ true
+ true
+
+
+
+
+
+ openTemplate
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+
+
+
+ treenode
+ org.apache.struts2.views.jsp.ui.TreeNodeTag
+ JSP
+
+
+
+ label
+ true
+ true
+
+
+
+
+
+ openTemplate
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ fielderror
+ org.apache.struts2.views.jsp.ui.FieldErrorTag
+ JSP
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ subset
+ org.apache.struts2.views.jsp.iterator.SubsetIteratorTag
+ JSP
+
+
+
+ count
+ false
+ true
+
+
+
+
+
+ source
+ false
+ true
+
+
+
+
+
+
+ start
+ false
+ true
+
+
+
+
+
+
+ decider
+ false
+ true
+
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+ elseif
+ org.apache.struts2.views.jsp.ElseIfTag
+ JSP
+
+
+
+ test
+ true
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ text
+ org.apache.struts2.views.jsp.TextTag
+ JSP
+
+
+
+ name
+ true
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
+ datepicker
+ org.apache.struts2.views.jsp.ui.DatePickerTag
+ JSP
+
+
+
+ language
+ false
+ true
+
+
+
+
+
+ format
+ false
+ true
+
+
+
+
+
+ showstime
+ false
+ true
+
+
+
+
+
+
+ singleclick
+ false
+ true
+
+
+
+
+
+ maxlength
+ false
+ true
+
+
+
+
+
+ maxLength
+ false
+ true
+
+
+
+
+
+ readonly
+ false
+ true
+
+
+
+
+
+ size
+ false
+ true
+
+
+
+
+
+ theme
+ false
+ true
+
+
+
+
+
+ templateDir
+ false
+ true
+
+
+
+
+
+
+ template
+ false
+ true
+
+
+
+
+
+ cssClass
+ false
+ true
+
+
+
+
+
+ cssStyle
+ false
+ true
+
+
+
+
+
+ title
+ false
+ true
+
+
+
+
+
+ disabled
+ false
+ true
+
+
+
+
+
+ label
+ false
+ true
+
+
+
+
+
+ labelposition
+ false
+ true
+
+
+
+
+
+ requiredposition
+ false
+ true
+
+
+
+
+
+ name
+ false
+ true
+
+
+
+
+
+ required
+ false
+ true
+
+
+
+
+
+
+ tabindex
+ false
+ true
+
+
+
+
+
+ value
+ false
+ true
+
+
+
+
+
+ onclick
+ false
+ true
+
+
+
+
+
+ ondblclick
+ false
+ true
+
+
+
+
+
+ onmousedown
+ false
+ true
+
+
+
+
+
+ onmouseup
+ false
+ true
+
+
+
+
+
+ onmouseover
+ false
+ true
+
+
+
+
+
+ onmousemove
+ false
+ true
+
+
+
+
+
+ onmouseout
+ false
+ true
+
+
+
+
+
+ onfocus
+ false
+ true
+
+
+
+
+
+ onblur
+ false
+ true
+
+
+
+
+
+ onkeypress
+ false
+ true
+
+
+
+
+
+ onkeydown
+ false
+ true
+
+
+
+
+
+ onkeyup
+ false
+ true
+
+
+
+
+
+ onselect
+ false
+ true
+
+
+
+
+
+ onchange
+ false
+ true
+
+
+
+
+
+ accesskey
+ false
+ true
+
+
+
+
+
+ tooltip
+ false
+ true
+
+
+
+
+
+ tooltipConfig
+ false
+ true
+
+
+
+
+
+ id
+ false
+ true
+
+
+
+
+
+
+
+
+
diff --git a/trunk/core/src/main/resources/org/apache/struts2/default.properties b/trunk/core/src/main/resources/org/apache/struts2/default.properties
new file mode 100644
index 000000000..275322c92
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/default.properties
@@ -0,0 +1,146 @@
+### START SNIPPET: complete_file
+
+### Struts default properties
+###(can be overridden by a struts.properties file in the root of the classpath)
+###
+
+### Specifies the Configuration used to configure Struts
+### one could extend org.apache.struts2.config.Configuration
+### to build one's customize way of getting the configurations parameters into Struts
+# struts.configuration=org.apache.struts2.config.DefaultConfiguration
+
+### This can be used to set your default locale and encoding scheme
+# struts.locale=en_US
+struts.i18n.encoding=UTF-8
+
+### if specified, the default object factory can be overridden here
+### Note: short-hand notation is supported in some cases, such as "spring"
+### Alternatively, you can provide a com.opensymphony.xwork2.ObjectFactory subclass name here
+# struts.objectFactory = spring
+
+### specifies the autoWiring logic when using the SpringObjectFactory.
+### valid values are: name, type, auto, and constructor (name is the default)
+struts.objectFactory.spring.autoWire = name
+
+### indicates to the struts-spring integration if Class instances should be cached
+### this should, until a future Spring release makes it possible, be left as true
+### unless you know exactly what you are doing!
+### valid values are: true, false (true is the default)
+struts.objectFactory.spring.useClassCache = true
+
+### if specified, the default object type determiner can be overridden here
+### Note: short-hand notation is supported in some cases, such as "tiger" or "notiger"
+### Alternatively, you can provide a com.opensymphony.xwork2.util.ObjectTypeDeterminer implementation name here
+### Note: if you have the xwork-tiger.jar within your classpath, GenericsObjectTypeDeterminer is used by default
+### To disable tiger support use the "notiger" property value here.
+#struts.objectTypeDeterminer = tiger
+#struts.objectTypeDeterminer = notiger
+
+### Parser to handle HTTP POST requests, encoded using the MIME-type multipart/form-data
+# struts.multipart.parser=cos
+# struts.multipart.parser=pell
+struts.multipart.parser=jakarta
+# uses javax.servlet.context.tempdir by default
+struts.multipart.saveDir=
+struts.multipart.maxSize=2097152
+
+### Load custom property files (does not override struts.properties!)
+# struts.custom.properties=application,org/apache/struts2/extension/custom
+
+### How request URLs are mapped to and from actions
+struts.mapper.class=org.apache.struts2.dispatcher.mapper.DefaultActionMapper
+
+### Used by the DefaultActionMapper
+### You may provide a comma separated list, e.g. struts.action.extension=action,jnlp,do
+struts.action.extension=action
+
+### Used by FilterDispatcher
+### If true then Struts serves static content from inside its jar.
+### If false then the static content must be available at /struts
+struts.serve.static=true
+
+### Used by FilterDispatcher
+### This is good for development where one wants changes to the static content be
+### fetch on each request.
+### NOTE: This will only have effect if struts.serve.static=true
+### If true -> Struts will write out header for static contents such that they will
+### be cached by web browsers (using Date, Cache-Content, Pragma, Expires)
+### headers).
+### If false -> Struts will write out header for static contents such that they are
+### NOT to be cached by web browser (using Cache-Content, Pragma, Expires
+### headers)
+struts.serve.static.browserCache=true
+
+### Set this to false if you wish to disable implicit dynamic method invocation
+### via the URL request. This includes URLs like foo!bar.action, as well as params
+### like method:bar (but not action:foo).
+### An alternative to implicit dynamic method invocation is to use wildcard
+### mappings, such as
+struts.enable.DynamicMethodInvocation = true
+
+### use alternative syntax that requires %{} in most places
+### to evaluate expressions for String attributes for tags
+struts.tag.altSyntax=true
+
+### when set to true, Struts will act much more friendly for developers. This
+### includes:
+### - struts.i18n.reload = true
+### - struts.configuration.xml.reload = true
+### - raising various debug or ignorable problems to errors
+### For example: normally a request to foo.action?someUnknownField=true should
+### be ignored (given that any value can come from the web and it
+### should not be trusted). However, during development, it may be
+### useful to know when these errors are happening and be told of
+### them right away.
+struts.devMode = false
+
+### when set to true, resource bundles will be reloaded on _every_ request.
+### this is good during development, but should never be used in production
+struts.i18n.reload=false
+
+### Standard UI theme
+### Change this to reflect which path should be used for JSP control tag templates by default
+struts.ui.theme=xhtml
+struts.ui.templateDir=template
+#sets the default template type. Either ftl, vm, or jsp
+struts.ui.templateSuffix=ftl
+
+### Configuration reloading
+### This will cause the configuration to reload struts.xml when it is changed
+struts.configuration.xml.reload=false
+
+### Location of velocity.properties file. defaults to velocity.properties
+# struts.velocity.configfile = velocity.properties
+
+### Comma separated list of VelocityContext classnames to chain to the StrutsVelocityContext
+# struts.velocity.contexts =
+
+### used to build URLs, such as the UrlTag
+struts.url.http.port = 80
+struts.url.https.port = 443
+### possible values are: none, get or all
+struts.url.includeParams = get
+
+### Load custom default resource bundles
+# struts.custom.i18n.resources=testmessages,testmessages2
+
+### workaround for some app servers that don't handle HttpServletRequest.getParameterMap()
+### often used for WebLogic, Orion, and OC4J
+struts.dispatcher.parametersWorkaround = false
+
+### configure the Freemarker Manager class to be used
+### Allows user to plug-in customised Freemarker Manager if necessary
+### MUST extends off org.apache.struts2.views.freemarker.FreemarkerManager
+#struts.freemarker.manager.classname=org.apache.struts2.views.freemarker.FreemarkerManager
+
+### See the StrutsBeanWrapper javadocs for more information
+struts.freemarker.wrapper.altMap=true
+
+### configure the XSLTResult class to use stylesheet caching.
+### Set to true for developers and false for production.
+struts.xslt.nocache=false
+
+### A list of configuration files automatically loaded by Struts
+struts.configuration.files=struts-default.xml,struts-plugin.xml,struts.xml
+
+### END SNIPPET: complete_file
diff --git a/trunk/core/src/main/resources/org/apache/struts2/dispatcher/error.ftl b/trunk/core/src/main/resources/org/apache/struts2/dispatcher/error.ftl
new file mode 100644
index 000000000..58049e81d
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/dispatcher/error.ftl
@@ -0,0 +1,123 @@
+
+
+ Struts Problem Report
+
+
+
+ Struts Problem Report
+
+ Struts has detected an unhandled exception:
+
+
+<#assign msgs = [] />
+<#list chain as ex>
+ <#if ex.message?exists>
+ <#assign msgs = [ex.message] + msgs/>
+ #if>
+#list>
+<#assign rootex = exception/>
+<#list chain as ex>
+ <#if (ex.location?exists && (ex.location != unknown))>
+ <#assign rootloc = ex.location/>
+ <#assign rootex = ex/>
+ <#else>
+ <#assign tmploc = locator.getLocation(ex) />
+ <#if (tmploc != unknown)>
+ <#assign rootloc = tmploc/>
+ <#assign rootex = ex/>
+ #if>
+ #if>
+#list>
+
+
+
+
+ Messages :
+
+ <#if (msgs?size > 1)>
+
+ <#list msgs as msg>
+ ${msg}
+ #list>
+
+ <#elseif (msgs?size == 1)>
+ ${msgs[0]}
+ #if>
+
+
+ <#if rootloc?exists>
+
+ File :
+ ${rootloc.URI}
+
+
+ Line number :
+ ${rootloc.lineNumber}
+
+ <#if (rootloc.columnNumber >= 0)>
+
+ Column number :
+ ${rootloc.columnNumber}
+
+ #if>
+ #if>
+
+
+
+
+<#if rootloc?exists>
+ <#assign snippet = rootloc.getSnippet(2) />
+ <#if (snippet?size > 0)>
+
+
+
+ <#list snippet as line>
+ <#if (line_index == 2)>
+ <#if (rootloc.columnNumber >= 3)>
+
${(line[0..(rootloc.columnNumber-3)]?html)}${(line[(rootloc.columnNumber-2)]?html)} <#if ((rootloc.columnNumber)${(line[(rootloc.columnNumber-1)..]?html)}#if>
+ <#else>
+
${line?html}
+ #if>
+ <#else>
+
${line?html}
+ #if>
+ #list>
+
+ #if>
+#if>
+
+
+
+
Stacktraces
+<#list chain as ex>
+
+
${ex}
+
+
+ <#list ex.stackTrace as frame>
+ ${frame}
+ #list>
+
+
+
+#list>
+
+
+
+
+
diff --git a/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/console.ftl b/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/console.ftl
new file mode 100644
index 000000000..14b39b5e0
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/console.ftl
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ ${debugXML}
+
+
+
diff --git a/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/webconsole.css b/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/webconsole.css
new file mode 100644
index 000000000..293e2fc87
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/webconsole.css
@@ -0,0 +1,19 @@
+.wc-results {
+ overflow: auto;
+ margin: 0px;
+ padding: 5px;
+ font-family: courier;
+ color: white;
+ background-color: black;
+ height: 400px;
+}
+.wc-results pre {
+ display: inline;
+}
+.wc-command {
+ margin: 0px;
+ font-family: courier;
+ color: white;
+ background-color: black;
+ width: 100%;
+}
diff --git a/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/webconsole.html b/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/webconsole.html
new file mode 100644
index 000000000..ce093c969
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/webconsole.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+OGNL Console
+
+
+
+
+
diff --git a/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/webconsole.js b/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/webconsole.js
new file mode 100644
index 000000000..2e7783b3f
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/interceptor/debugging/webconsole.js
@@ -0,0 +1,58 @@
+ function printResult(result_string)
+ {
+ var result_div = document.getElementById('wc-result');
+ var result_array = result_string.split('\n');
+
+ var new_command = document.getElementById('wc-command').value;
+ result_div.appendChild(document.createTextNode(new_command));
+ result_div.appendChild(document.createElement('br'));
+
+ for (var line_index in result_array) {
+ var result_wrap = document.createElement('pre')
+ line = document.createTextNode(result_array[line_index]);
+ result_wrap.appendChild(line);
+ result_div.appendChild(result_wrap);
+ result_div.appendChild(document.createElement('br'));
+
+ }
+ result_div.appendChild(document.createTextNode(':-> '));
+
+ result_div.scrollTop = result_div.scrollHeight;
+ document.getElementById('wc-command').value = '';
+ }
+
+ function keyEvent(event)
+ {
+ switch(event.keyCode){
+ case 13:
+ var the_shell_command = document.getElementById('wc-command').value;
+ if (the_shell_command) {
+ commands_history[commands_history.length] = the_shell_command;
+ history_pointer = commands_history.length;
+ var the_url = window.opener.location.pathname + '?debug=command&expression='+escape(the_shell_command);
+ dojo.io.bind({
+ url: the_url,
+ load: function(type, data, evt){ printResult(data); },
+ mimetype: "text/plain"
+ });
+ }
+ break;
+ case 38: // this is the arrow up
+ if (history_pointer > 0) {
+ history_pointer--;
+ document.getElementById('wc-command').value = commands_history[history_pointer];
+ }
+ break;
+ case 40: // this is the arrow down
+ if (history_pointer < commands_history.length - 1 ) {
+ history_pointer++;
+ document.getElementById('wc-command').value = commands_history[history_pointer];
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ var commands_history = new Array();
+ var history_pointer;
diff --git a/trunk/core/src/main/resources/org/apache/struts2/interceptor/package.html b/trunk/core/src/main/resources/org/apache/struts2/interceptor/package.html
new file mode 100644
index 000000000..1413ca1b1
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/interceptor/package.html
@@ -0,0 +1 @@
+Web specific interceptor classes.
diff --git a/trunk/core/src/main/resources/org/apache/struts2/interceptor/wait.ftl b/trunk/core/src/main/resources/org/apache/struts2/interceptor/wait.ftl
new file mode 100644
index 000000000..1d1869f84
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/interceptor/wait.ftl
@@ -0,0 +1,11 @@
+
+
+ "/>
+
+
+ Please wait while we process your request...
+
+
+ This page will reload automatically and display your request when it is completed.
+
+
diff --git a/trunk/core/src/main/resources/org/apache/struts2/package.html b/trunk/core/src/main/resources/org/apache/struts2/package.html
new file mode 100644
index 000000000..5ed3ff00b
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/package.html
@@ -0,0 +1 @@
+Main Struts interfaces and classes.
diff --git a/trunk/core/src/main/resources/org/apache/struts2/sitegraph/sitegraph-usage.txt b/trunk/core/src/main/resources/org/apache/struts2/sitegraph/sitegraph-usage.txt
new file mode 100644
index 000000000..258d01972
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/sitegraph/sitegraph-usage.txt
@@ -0,0 +1,7 @@
+// START SNIPPET: sitegraph-usage
+Usage: -config CONFIG_DIR -views VIEWS_DIRS -output OUTPUT [-ns NAMESPACE]
+ CONFIG_DIR => a directory containing struts.xml
+ VIEWS_DIRS => comma seperated list of dirs containing JSPs, VMs, etc
+ OUPUT => the directory where the output should go
+ NAMESPACE => the namespace path restriction (/, /foo, etc)
+// END SNIPPET: sitegraph-usage
diff --git a/trunk/core/src/main/resources/org/apache/struts2/static/CommonFunctions.js b/trunk/core/src/main/resources/org/apache/struts2/static/CommonFunctions.js
new file mode 100644
index 000000000..25e609403
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/static/CommonFunctions.js
@@ -0,0 +1,97 @@
+
+/**
+ * Methods for the tabbed component
+ */
+var unselectedClass = "tab_default tab_unselected";
+var unselectedContentsClass = "tab_contents_hidden";
+var unselectedOverClass = "tab_default tab_unselected tab_unselected_over";
+var selectedClass = "tab_default tab_selected";
+var selectedContentsClass = "tab_contents_header";
+
+function mouseIn(tab) {
+ var className = tab.className;
+ if (className.indexOf('unselected') > -1) {
+ className = unselectedOverClass;
+ tab.className = className;
+ }
+}
+
+function mouseOut(tab) {
+ var className = tab.className;
+ if (className.indexOf('unselected') > -1) {
+ className = unselectedClass;
+ tab.className = className;
+ }
+}
+
+/*
+ * An object that represents a tabbed page.
+ *
+ * @param htmlId the id of the element that represents the tab page
+ * @param remote whether this is a remote element and needs refreshing
+ */
+function TabContent( htmlId, remote ) {
+
+ this.elementId = htmlId;
+ this.isRemote = remote;
+ var selected = false;
+ var self = this;
+
+ /*
+ * Shows or hides this page depending on whether the visible
+ * tab id matches this objects id.
+ *
+ * @param visibleTabId the id of the tab that was selected
+ */
+ this.updateVisibility = function( visibleTabId ) {
+ var thElement = document.getElementById( 'tab_header_'+self.elementId );
+ var tcElement = document.getElementById( 'tab_contents_'+self.elementId );
+ if (!selected && visibleTabId==self.elementId) {
+ thElement.className = selectedClass;
+ tcElement.className = selectedContentsClass;
+ self.selected = true;
+
+ } else {
+ thElement.className = unselectedClass;
+ tcElement.className = unselectedContentsClass;
+ self.selected = false;
+ }
+ if (self.isRemote==true && visibleTabId==self.elementId) {
+ var rel = window['tab_contents_update_'+self.elementId];
+ // If the first tab is a remote tab, rel is null on initial loading...
+ // so don't try to call a method that doesn't exist. This is only
+ // for IE, and the workaround is to use a
+ // as the content of the DIV.
+ if (rel.bind)
+ rel.bind();
+ }
+ }
+
+}
+
+/**
+ * Checks whether the current form include an ajax-ified submit button, if so
+ * we return true (otherwise false).
+ *
+ * @param form the HTML form element to check
+ */
+function isAjaxFormSubmit( form ) {
+ // we check whether this exists
+ //
+ var thisForm = document.getElementById(form.id);
+ var matchUrl = /\s+dojoAttachPoint/;
+ if( thisForm.innerHTML.match(matchUrl) ) {
+ return false;
+ }
+ for( i=0; i
+
+Depending on the edition that you have downloaded, this base dojo.js file may or
+may not include the modules you wish to use in your application. To ensure that
+they are available, use dojo.require() to request them. A very rich application
+might include:
+
+
+
+
+Note that only those modules which are *not* already "baked in" to dojo.js by
+the edition's build process are requested by dojo.require(). This helps make
+your application faster without forcing you to use a build tool while in
+development. See "Building Dojo" and "Working From Source" for more details.
+
+
+Compatibility
+-------------
+
+In addition to it's suite of unit-tests for core system components, Dojo has
+been tested on almost every modern browser, including:
+
+ - IE 5.5+
+ - Mozilla 1.2+, Firefox 1.0+
+ - Safari 1.3.9+
+ - Konqueror 3.4+
+ - Opera 8.5+
+
+Note that some widgets and features may not preform exactly the same on every
+browser due to browser implementation differences.
+
+For those looking to use Dojo in non-browser environments, please see "Working
+From Source".
+
+
+Documentation and Getting Help
+------------------------------
+
+Articles outlining major Dojo systems are linked from:
+
+ http://dojotoolkit.org/docs/
+
+Toolkit APIs are listed in outline form at:
+
+ http://dojotoolkit.org/docs/apis/
+
+And documented in full at:
+
+ http://manual.dojotoolkit.org/
+
+The project also maintains a JotSpot Wiki at:
+
+ http://dojo.jot.com/
+
+A FAQ has been extracted from mailing list traffic:
+
+ http://dojo.jot.com/FAQ
+
+And the main Dojo user mailing list is archived and made searchable at:
+
+ http://news.gmane.org/gmane.comp.web.dojo.user/
+
+You can sign up for this list, which is a great place to ask questions, at:
+
+ http://dojotoolkit.org/mailman/listinfo/dojo-interest
+
+The Dojo developers also tend to hang out in IRC and help people with Dojo
+problems. You're most likely to find them at:
+
+ irc.freenode.net #dojo
+
+Note that 2PM Wed PST in this channel is reserved for a weekly meeting between
+project developers, although anyone is welcome to participate.
+
+
+Working From Source
+-------------------
+
+The core of Dojo is a powerful package system that allows developers to optimize
+Dojo for deployment while using *exactly the same* application code in
+development. Therefore, working from source is almost exactly like working from
+a pre-built edition. Pre-built editions are significantly faster to load than
+working from source, but are not as flexible when in development.
+
+There are multiple ways to get the source. Nightly snapshots of the Dojo source
+repository are available at:
+
+ http://archive.dojotoolkit.org/nightly.tgz
+
+Anonymous Subversion access is also available:
+
+ %> svn co http://dojootoolkit.org/svn/dojo/trunk/
+
+Each of these sources will include some extra directories not included in the
+pre-packaged editions, including command-line tests and build tools for
+constructing your own packages.
+
+Running the command-line unit test suite requires Ant 1.6. If it is installed
+and in your path, you can run the tests using:
+
+ %> cd buildscripts
+ %> ant test
+
+The command-line test harness makes use of Rhino, a JavaScript interpreter
+written in Java. Once you have a copy of Dojo's source tree, you have a copy of
+Rhino. From the root directory, you can use Rhino interactively to load Dojo:
+
+ %> java -jar buildscripts/lib/js.jar
+ Rhino 1.5 release 3 2002 01 27
+ js> load("dojo.js");
+ js> print(dojo);
+ [object Object]
+ js> quit();
+
+This environment is wonderful for testing raw JavaScript functionality in, or
+even for scripting your system. Since Rhino has full access to anything in
+Java's classpath, the sky is the limit!
+
+Building Dojo
+-------------
+
+Dojo requires Ant 1.6.x in order to build correctly. While using Dojo from
+source does *NOT* require that you make a build, speeding up your application by
+constructing a custom profile build does.
+
+Once you have Ant and a source snapshot of Dojo, you can make your own profile
+build ("edition") which includes only those modules your application uses by
+customizing one of the files in:
+
+ [dojo]/buildscripts/profiles/
+
+These files are named *.profile.js and each one contains a list of modules to
+include in a build. If we created a new profile called "test.profile.js", we
+could then make a profile build using it by doing:
+
+ %> cd buildscripts
+ %> ant -Dprofile=test -Ddocless=true release intern-strings
+
+If the build is successful, your newly minted and compressed profile build will
+be placed in [dojo]/releae/dojo/
+
+-------------------------------------------------------------------------------
+Copyright (c) 2004-2005, The Dojo Foundation, All Rights Reserved
+
+vim:ts=4:et:tw=80:shiftwidth=4:
diff --git a/trunk/core/src/main/resources/org/apache/struts2/static/dojo/build.txt b/trunk/core/src/main/resources/org/apache/struts2/static/dojo/build.txt
new file mode 100644
index 000000000..28c43186f
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/static/dojo/build.txt
@@ -0,0 +1,13 @@
+
+Files baked into this package:
+
+../src/bootstrap1.js,
+../src/hostenv_browser.js,
+../src/bootstrap2.js,
+../src/lang.js,
+../src/string.js,
+../src/io.js,
+../src/dom.js,
+../src/io/BrowserIO.js
+
+
\ No newline at end of file
diff --git a/trunk/core/src/main/resources/org/apache/struts2/static/dojo/dojo.js b/trunk/core/src/main/resources/org/apache/struts2/static/dojo/dojo.js
new file mode 100644
index 000000000..df8c9aae4
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/static/dojo/dojo.js
@@ -0,0 +1,2521 @@
+/*
+ Copyright (c) 2004-2005, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/*
+ This is a compiled version of Dojo, built for deployment and not for
+ development. To get an editable version, please visit:
+
+ http://dojotoolkit.org
+
+ for documentation and information on getting the source.
+*/
+
+var dj_global=this;
+function dj_undef(_1,_2){
+if(!_2){
+_2=dj_global;
+}
+return (typeof _2[_1]=="undefined");
+}
+if(dj_undef("djConfig")){
+var djConfig={};
+}
+var dojo;
+if(dj_undef("dojo")){
+dojo={};
+}
+dojo.version={major:0,minor:2,patch:2,flag:"",revision:Number("$Rev: 2836 $".match(/[0-9]+/)[0]),toString:function(){
+with(dojo.version){
+return major+"."+minor+"."+patch+flag+" ("+revision+")";
+}
+}};
+dojo.evalObjPath=function(_3,_4){
+if(typeof _3!="string"){
+return dj_global;
+}
+if(_3.indexOf(".")==-1){
+if((dj_undef(_3,dj_global))&&(_4)){
+dj_global[_3]={};
+}
+return dj_global[_3];
+}
+var _5=_3.split(/\./);
+var _6=dj_global;
+for(var i=0;i<_5.length;++i){
+if(!_4){
+_6=_6[_5[i]];
+if((typeof _6=="undefined")||(!_6)){
+return _6;
+}
+}else{
+if(dj_undef(_5[i],_6)){
+_6[_5[i]]={};
+}
+_6=_6[_5[i]];
+}
+}
+return _6;
+};
+dojo.errorToString=function(_8){
+return ((!dj_undef("message",_8))?_8.message:(dj_undef("description",_8)?_8:_8.description));
+};
+dojo.raise=function(_9,_a){
+if(_a){
+_9=_9+": "+dojo.errorToString(_a);
+}
+var he=dojo.hostenv;
+if((!dj_undef("hostenv",dojo))&&(!dj_undef("println",dojo.hostenv))){
+dojo.hostenv.println("FATAL: "+_9);
+}
+throw Error(_9);
+};
+dj_throw=dj_rethrow=function(m,e){
+dojo.deprecated("dj_throw and dj_rethrow deprecated, use dojo.raise instead");
+dojo.raise(m,e);
+};
+dojo.debug=function(){
+if(!djConfig.isDebug){
+return;
+}
+var _e=arguments;
+if(dj_undef("println",dojo.hostenv)){
+dojo.raise("dojo.debug not available (yet?)");
+}
+var _f=dj_global["jum"]&&!dj_global["jum"].isBrowser;
+var s=[(_f?"":"DEBUG: ")];
+for(var i=0;i<_e.length;++i){
+if(!false&&_e[i] instanceof Error){
+var msg="["+_e[i].name+": "+dojo.errorToString(_e[i])+(_e[i].fileName?", file: "+_e[i].fileName:"")+(_e[i].lineNumber?", line: "+_e[i].lineNumber:"")+"]";
+}else{
+try{
+var msg=String(_e[i]);
+}
+catch(e){
+if(dojo.render.html.ie){
+var msg="[ActiveXObject]";
+}else{
+var msg="[unknown]";
+}
+}
+}
+s.push(msg);
+}
+if(_f){
+jum.debug(s.join(" "));
+}else{
+dojo.hostenv.println(s.join(" "));
+}
+};
+dojo.debugShallow=function(obj){
+if(!djConfig.isDebug){
+return;
+}
+dojo.debug("------------------------------------------------------------");
+dojo.debug("Object: "+obj);
+for(i in obj){
+dojo.debug(i+": "+obj[i]);
+}
+dojo.debug("------------------------------------------------------------");
+};
+var dj_debug=dojo.debug;
+function dj_eval(s){
+return dj_global.eval?dj_global.eval(s):eval(s);
+}
+dj_unimplemented=dojo.unimplemented=function(_15,_16){
+var _17="'"+_15+"' not implemented";
+if((!dj_undef(_16))&&(_16)){
+_17+=" "+_16;
+}
+dojo.raise(_17);
+};
+dj_deprecated=dojo.deprecated=function(_18,_19,_1a){
+var _1b="DEPRECATED: "+_18;
+if(_19){
+_1b+=" "+_19;
+}
+if(_1a){
+_1b+=" -- will be removed in version: "+_1a;
+}
+dojo.debug(_1b);
+};
+dojo.inherits=function(_1c,_1d){
+if(typeof _1d!="function"){
+dojo.raise("superclass: "+_1d+" borken");
+}
+_1c.prototype=new _1d();
+_1c.prototype.constructor=_1c;
+_1c.superclass=_1d.prototype;
+_1c["super"]=_1d.prototype;
+};
+dj_inherits=function(_1e,_1f){
+dojo.deprecated("dj_inherits deprecated, use dojo.inherits instead");
+dojo.inherits(_1e,_1f);
+};
+dojo.render=(function(){
+function vscaffold(_20,_21){
+var tmp={capable:false,support:{builtin:false,plugin:false},prefixes:_20};
+for(var x in _21){
+tmp[x]=false;
+}
+return tmp;
+}
+return {name:"",ver:dojo.version,os:{win:false,linux:false,osx:false},html:vscaffold(["html"],["ie","opera","khtml","safari","moz"]),svg:vscaffold(["svg"],["corel","adobe","batik"]),vml:vscaffold(["vml"],["ie"]),swf:vscaffold(["Swf","Flash","Mm"],["mm"]),swt:vscaffold(["Swt"],["ibm"])};
+})();
+dojo.hostenv=(function(){
+var _24={isDebug:false,allowQueryConfig:false,baseScriptUri:"",baseRelativePath:"",libraryScriptUri:"",iePreventClobber:false,ieClobberMinimal:true,preventBackButtonFix:true,searchIds:[],parseWidgets:true};
+if(typeof djConfig=="undefined"){
+djConfig=_24;
+}else{
+for(var _25 in _24){
+if(typeof djConfig[_25]=="undefined"){
+djConfig[_25]=_24[_25];
+}
+}
+}
+var djc=djConfig;
+function _def(obj,_28,def){
+return (dj_undef(_28,obj)?def:obj[_28]);
+}
+return {name_:"(unset)",version_:"(unset)",pkgFileName:"__package__",loading_modules_:{},loaded_modules_:{},addedToLoadingCount:[],removedFromLoadingCount:[],inFlightCount:0,modulePrefixes_:{dojo:{name:"dojo",value:"src"}},setModulePrefix:function(_2a,_2b){
+this.modulePrefixes_[_2a]={name:_2a,value:_2b};
+},getModulePrefix:function(_2c){
+var mp=this.modulePrefixes_;
+if((mp[_2c])&&(mp[_2c]["name"])){
+return mp[_2c].value;
+}
+return _2c;
+},getTextStack:[],loadUriStack:[],loadedUris:[],post_load_:false,modulesLoadedListeners:[],getName:function(){
+return this.name_;
+},getVersion:function(){
+return this.version_;
+},getText:function(uri){
+dojo.unimplemented("getText","uri="+uri);
+},getLibraryScriptUri:function(){
+dojo.unimplemented("getLibraryScriptUri","");
+}};
+})();
+dojo.hostenv.getBaseScriptUri=function(){
+if(djConfig.baseScriptUri.length){
+return djConfig.baseScriptUri;
+}
+var uri=new String(djConfig.libraryScriptUri||djConfig.baseRelativePath);
+if(!uri){
+dojo.raise("Nothing returned by getLibraryScriptUri(): "+uri);
+}
+var _30=uri.lastIndexOf("/");
+djConfig.baseScriptUri=djConfig.baseRelativePath;
+return djConfig.baseScriptUri;
+};
+dojo.hostenv.setBaseScriptUri=function(uri){
+djConfig.baseScriptUri=uri;
+};
+dojo.hostenv.loadPath=function(_32,_33,cb){
+if((_32.charAt(0)=="/")||(_32.match(/^\w+:/))){
+dojo.raise("relpath '"+_32+"'; must be relative");
+}
+var uri=this.getBaseScriptUri()+_32;
+if(djConfig.cacheBust&&dojo.render.html.capable){
+uri+="?"+String(djConfig.cacheBust).replace(/\W+/g,"");
+}
+try{
+return ((!_33)?this.loadUri(uri,cb):this.loadUriAndCheck(uri,_33,cb));
+}
+catch(e){
+dojo.debug(e);
+return false;
+}
+};
+dojo.hostenv.loadUri=function(uri,cb){
+if(this.loadedUris[uri]){
+return;
+}
+var _38=this.getText(uri,null,true);
+if(_38==null){
+return 0;
+}
+this.loadedUris[uri]=true;
+var _39=dj_eval(_38);
+return 1;
+};
+dojo.hostenv.loadUriAndCheck=function(uri,_3b,cb){
+var ok=true;
+try{
+ok=this.loadUri(uri,cb);
+}
+catch(e){
+dojo.debug("failed loading ",uri," with error: ",e);
+}
+return ((ok)&&(this.findModule(_3b,false)))?true:false;
+};
+dojo.loaded=function(){
+};
+dojo.hostenv.loaded=function(){
+this.post_load_=true;
+var mll=this.modulesLoadedListeners;
+for(var x=0;x1){
+dojo.hostenv.modulesLoadedListeners.push(function(){
+obj[_41]();
+});
+}
+}
+};
+dojo.hostenv.modulesLoaded=function(){
+if(this.post_load_){
+return;
+}
+if((this.loadUriStack.length==0)&&(this.getTextStack.length==0)){
+if(this.inFlightCount>0){
+dojo.debug("files still in flight!");
+return;
+}
+if(typeof setTimeout=="object"){
+setTimeout("dojo.hostenv.loaded();",0);
+}else{
+dojo.hostenv.loaded();
+}
+}
+};
+dojo.hostenv.moduleLoaded=function(_42){
+var _43=dojo.evalObjPath((_42.split(".").slice(0,-1)).join("."));
+this.loaded_modules_[(new String(_42)).toLowerCase()]=_43;
+};
+dojo.hostenv._global_omit_module_check=false;
+dojo.hostenv.loadModule=function(_44,_45,_46){
+if(!_44){
+return;
+}
+_46=this._global_omit_module_check||_46;
+var _47=this.findModule(_44,false);
+if(_47){
+return _47;
+}
+if(dj_undef(_44,this.loading_modules_)){
+this.addedToLoadingCount.push(_44);
+}
+this.loading_modules_[_44]=1;
+var _48=_44.replace(/\./g,"/")+".js";
+var _49=_44.split(".");
+var _4a=_44.split(".");
+for(var i=_49.length-1;i>0;i--){
+var _4c=_49.slice(0,i).join(".");
+var _4d=this.getModulePrefix(_4c);
+if(_4d!=_4c){
+_49.splice(0,i,_4d);
+break;
+}
+}
+var _4e=_49[_49.length-1];
+if(_4e=="*"){
+_44=(_4a.slice(0,-1)).join(".");
+while(_49.length){
+_49.pop();
+_49.push(this.pkgFileName);
+_48=_49.join("/")+".js";
+if(_48.charAt(0)=="/"){
+_48=_48.slice(1);
+}
+ok=this.loadPath(_48,((!_46)?_44:null));
+if(ok){
+break;
+}
+_49.pop();
+}
+}else{
+_48=_49.join("/")+".js";
+_44=_4a.join(".");
+var ok=this.loadPath(_48,((!_46)?_44:null));
+if((!ok)&&(!_45)){
+_49.pop();
+while(_49.length){
+_48=_49.join("/")+".js";
+ok=this.loadPath(_48,((!_46)?_44:null));
+if(ok){
+break;
+}
+_49.pop();
+_48=_49.join("/")+"/"+this.pkgFileName+".js";
+if(_48.charAt(0)=="/"){
+_48=_48.slice(1);
+}
+ok=this.loadPath(_48,((!_46)?_44:null));
+if(ok){
+break;
+}
+}
+}
+if((!ok)&&(!_46)){
+dojo.raise("Could not load '"+_44+"'; last tried '"+_48+"'");
+}
+}
+if(!_46){
+_47=this.findModule(_44,false);
+if(!_47){
+dojo.raise("symbol '"+_44+"' is not defined after loading '"+_48+"'");
+}
+}
+return _47;
+};
+dojo.hostenv.startPackage=function(_50){
+var _51=_50.split(/\./);
+if(_51[_51.length-1]=="*"){
+_51.pop();
+}
+return dojo.evalObjPath(_51.join("."),true);
+};
+dojo.hostenv.findModule=function(_52,_53){
+var lmn=(new String(_52)).toLowerCase();
+if(this.loaded_modules_[lmn]){
+return this.loaded_modules_[lmn];
+}
+var _55=dojo.evalObjPath(_52);
+if((_52)&&(typeof _55!="undefined")&&(_55)){
+this.loaded_modules_[lmn]=_55;
+return _55;
+}
+if(_53){
+dojo.raise("no loaded module named '"+_52+"'");
+}
+return null;
+};
+if(typeof window=="undefined"){
+dojo.raise("no window object");
+}
+(function(){
+if(djConfig.allowQueryConfig){
+var _56=document.location.toString();
+var _57=_56.split("?",2);
+if(_57.length>1){
+var _58=_57[1];
+var _59=_58.split("&");
+for(var x in _59){
+var sp=_59[x].split("=");
+if((sp[0].length>9)&&(sp[0].substr(0,9)=="djConfig.")){
+var opt=sp[0].substr(9);
+try{
+djConfig[opt]=eval(sp[1]);
+}
+catch(e){
+djConfig[opt]=sp[1];
+}
+}
+}
+}
+}
+if(((djConfig["baseScriptUri"]=="")||(djConfig["baseRelativePath"]==""))&&(document&&document.getElementsByTagName)){
+var _5d=document.getElementsByTagName("script");
+var _5e=/(__package__|dojo)\.js([\?\.]|$)/i;
+for(var i=0;i<_5d.length;i++){
+var src=_5d[i].getAttribute("src");
+if(!src){
+continue;
+}
+var m=src.match(_5e);
+if(m){
+root=src.substring(0,m.index);
+if(!this["djConfig"]){
+djConfig={};
+}
+if(djConfig["baseScriptUri"]==""){
+djConfig["baseScriptUri"]=root;
+}
+if(djConfig["baseRelativePath"]==""){
+djConfig["baseRelativePath"]=root;
+}
+break;
+}
+}
+}
+var dr=dojo.render;
+var drh=dojo.render.html;
+var dua=drh.UA=navigator.userAgent;
+var dav=drh.AV=navigator.appVersion;
+var t=true;
+var f=false;
+drh.capable=t;
+drh.support.builtin=t;
+dr.ver=parseFloat(drh.AV);
+dr.os.mac=dav.indexOf("Macintosh")>=0;
+dr.os.win=dav.indexOf("Windows")>=0;
+dr.os.linux=dav.indexOf("X11")>=0;
+drh.opera=dua.indexOf("Opera")>=0;
+drh.khtml=(dav.indexOf("Konqueror")>=0)||(dav.indexOf("Safari")>=0);
+drh.safari=dav.indexOf("Safari")>=0;
+var _68=dua.indexOf("Gecko");
+drh.mozilla=drh.moz=(_68>=0)&&(!drh.khtml);
+if(drh.mozilla){
+drh.geckoVersion=dua.substring(_68+6,_68+14);
+}
+drh.ie=(document.all)&&(!drh.opera);
+drh.ie50=drh.ie&&dav.indexOf("MSIE 5.0")>=0;
+drh.ie55=drh.ie&&dav.indexOf("MSIE 5.5")>=0;
+drh.ie60=drh.ie&&dav.indexOf("MSIE 6.0")>=0;
+dr.vml.capable=drh.ie;
+dr.svg.capable=f;
+dr.svg.support.plugin=f;
+dr.svg.support.builtin=f;
+dr.svg.adobe=f;
+if(document.implementation&&document.implementation.hasFeature&&document.implementation.hasFeature("org.w3c.dom.svg","1.0")){
+dr.svg.capable=t;
+dr.svg.support.builtin=t;
+dr.svg.support.plugin=f;
+dr.svg.adobe=f;
+}else{
+if(navigator.mimeTypes&&navigator.mimeTypes.length>0){
+var _69=navigator.mimeTypes["image/svg+xml"]||navigator.mimeTypes["image/svg"]||navigator.mimeTypes["image/svg-xml"];
+if(_69){
+dr.svg.adobe=_69&&_69.enabledPlugin&&_69.enabledPlugin.description&&(_69.enabledPlugin.description.indexOf("Adobe")>-1);
+if(dr.svg.adobe){
+dr.svg.capable=t;
+dr.svg.support.plugin=t;
+}
+}
+}else{
+if(drh.ie&&dr.os.win){
+var _69=f;
+try{
+var _6a=new ActiveXObject("Adobe.SVGCtl");
+_69=t;
+}
+catch(e){
+}
+if(_69){
+dr.svg.capable=t;
+dr.svg.support.plugin=t;
+dr.svg.adobe=t;
+}
+}else{
+dr.svg.capable=f;
+dr.svg.support.plugin=f;
+dr.svg.adobe=f;
+}
+}
+}
+})();
+dojo.hostenv.startPackage("dojo.hostenv");
+dojo.hostenv.name_="browser";
+dojo.hostenv.searchIds=[];
+var DJ_XMLHTTP_PROGIDS=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"];
+dojo.hostenv.getXmlhttpObject=function(){
+var _6b=null;
+var _6c=null;
+try{
+_6b=new XMLHttpRequest();
+}
+catch(e){
+}
+if(!_6b){
+for(var i=0;i<3;++i){
+var _6e=DJ_XMLHTTP_PROGIDS[i];
+try{
+_6b=new ActiveXObject(_6e);
+}
+catch(e){
+_6c=e;
+}
+if(_6b){
+DJ_XMLHTTP_PROGIDS=[_6e];
+break;
+}
+}
+}
+if(!_6b){
+return dojo.raise("XMLHTTP not available",_6c);
+}
+return _6b;
+};
+dojo.hostenv.getText=function(uri,_70,_71){
+var _72=this.getXmlhttpObject();
+if(_70){
+_72.onreadystatechange=function(){
+if((4==_72.readyState)&&(_72["status"])){
+if(_72.status==200){
+_70(_72.responseText);
+}
+}
+};
+}
+_72.open("GET",uri,_70?true:false);
+_72.send(null);
+if(_70){
+return null;
+}
+return _72.responseText;
+};
+dojo.hostenv.defaultDebugContainerId="dojoDebug";
+dojo.hostenv._println_buffer=[];
+dojo.hostenv._println_safe=false;
+dojo.hostenv.println=function(_73){
+if(!dojo.hostenv._println_safe){
+dojo.hostenv._println_buffer.push(_73);
+}else{
+try{
+var _74=document.getElementById(djConfig.debugContainerId?djConfig.debugContainerId:dojo.hostenv.defaultDebugContainerId);
+if(!_74){
+_74=document.getElementsByTagName("body")[0]||document.body;
+}
+var div=document.createElement("div");
+div.appendChild(document.createTextNode(_73));
+_74.appendChild(div);
+}
+catch(e){
+try{
+document.write(""+_73+"
");
+}
+catch(e2){
+window.status=_73;
+}
+}
+}
+};
+dojo.addOnLoad(function(){
+dojo.hostenv._println_safe=true;
+while(dojo.hostenv._println_buffer.length>0){
+dojo.hostenv.println(dojo.hostenv._println_buffer.shift());
+}
+});
+function dj_addNodeEvtHdlr(_76,_77,fp,_79){
+var _7a=_76["on"+_77]||function(){
+};
+_76["on"+_77]=function(){
+fp.apply(_76,arguments);
+_7a.apply(_76,arguments);
+};
+return true;
+}
+dj_addNodeEvtHdlr(window,"load",function(){
+if(dojo.render.html.ie){
+dojo.hostenv.makeWidgets();
+}
+dojo.hostenv.modulesLoaded();
+});
+dojo.hostenv.makeWidgets=function(){
+var _7b=[];
+if(djConfig.searchIds&&djConfig.searchIds.length>0){
+_7b=_7b.concat(djConfig.searchIds);
+}
+if(dojo.hostenv.searchIds&&dojo.hostenv.searchIds.length>0){
+_7b=_7b.concat(dojo.hostenv.searchIds);
+}
+if((djConfig.parseWidgets)||(_7b.length>0)){
+if(dojo.evalObjPath("dojo.widget.Parse")){
+try{
+var _7c=new dojo.xml.Parse();
+if(_7b.length>0){
+for(var x=0;x<_7b.length;x++){
+var _7e=document.getElementById(_7b[x]);
+if(!_7e){
+continue;
+}
+var _7f=_7c.parseElement(_7e,null,true);
+dojo.widget.getParser().createComponents(_7f);
+}
+}else{
+if(djConfig.parseWidgets){
+var _7f=_7c.parseElement(document.getElementsByTagName("body")[0]||document.body,null,true);
+dojo.widget.getParser().createComponents(_7f);
+}
+}
+}
+catch(e){
+dojo.debug("auto-build-widgets error:",e);
+}
+}
+}
+};
+dojo.hostenv.modulesLoadedListeners.push(function(){
+if(!dojo.render.html.ie){
+dojo.hostenv.makeWidgets();
+}
+});
+try{
+if(dojo.render.html.ie){
+document.write("");
+document.write("");
+}
+}
+catch(e){
+}
+dojo.hostenv.writeIncludes=function(){
+};
+dojo.hostenv.byId=dojo.byId=function(id,doc){
+if(typeof id=="string"||id instanceof String){
+if(!doc){
+doc=document;
+}
+return doc.getElementById(id);
+}
+return id;
+};
+dojo.hostenv.byIdArray=dojo.byIdArray=function(){
+var ids=[];
+for(var i=0;i=0;i--){
+if(arr[i]===val){
+return i;
+}
+}
+}else{
+for(var i=arr.length-1;i>=0;i--){
+if(arr[i]==val){
+return i;
+}
+}
+}
+return -1;
+};
+dojo.lang.lastIndexOf=dojo.lang.findLast;
+dojo.lang.inArray=function(arr,val){
+return dojo.lang.find(arr,val)>-1;
+};
+dojo.lang.getNameInObj=function(ns,_d4){
+if(!ns){
+ns=dj_global;
+}
+for(var x in ns){
+if(ns[x]===_d4){
+return new String(x);
+}
+}
+return null;
+};
+dojo.lang.has=function(obj,_d7){
+return (typeof obj[_d7]!=="undefined");
+};
+dojo.lang.isEmpty=function(obj){
+if(dojo.lang.isObject(obj)){
+var tmp={};
+var _da=0;
+for(var x in obj){
+if(obj[x]&&(!tmp[x])){
+_da++;
+break;
+}
+}
+return (_da==0);
+}else{
+if(dojo.lang.isArrayLike(obj)||dojo.lang.isString(obj)){
+return obj.length==0;
+}
+}
+};
+dojo.lang.forEach=function(arr,_dd,_de){
+var _df=dojo.lang.isString(arr);
+if(_df){
+arr=arr.split("");
+}
+var il=arr.length;
+for(var i=0;i<((_de)?il:arr.length);i++){
+if(_dd(arr[i],i,arr)=="break"){
+break;
+}
+}
+};
+dojo.lang.map=function(arr,obj,_e4){
+var _e5=dojo.lang.isString(arr);
+if(_e5){
+arr=arr.split("");
+}
+if(dojo.lang.isFunction(obj)&&(!_e4)){
+_e4=obj;
+obj=dj_global;
+}else{
+if(dojo.lang.isFunction(obj)&&_e4){
+var _e6=obj;
+obj=_e4;
+_e4=_e6;
+}
+}
+if(Array.map){
+var _e7=Array.map(arr,_e4,obj);
+}else{
+var _e7=[];
+for(var i=0;i=3){
+dojo.raise("thisObject doesn't exist!");
+}
+_f3=dj_global;
+}
+for(var i=0;i=3){
+dojo.raise("thisObject doesn't exist!");
+}
+_f8=dj_global;
+}
+for(var i=0;i=3){
+dojo.raise("thisObject doesn't exist!");
+}
+_fd=dj_global;
+}
+var _ff=[];
+for(var i=0;i0){
+return str.replace(/^\s+/,"");
+}else{
+if(wh<0){
+return str.replace(/\s+$/,"");
+}else{
+return str.replace(/^\s+|\s+$/g,"");
+}
+}
+};
+dojo.string.trimStart=function(str){
+return dojo.string.trim(str,1);
+};
+dojo.string.trimEnd=function(str){
+return dojo.string.trim(str,-1);
+};
+dojo.string.paramString=function(str,_122,_123){
+for(var name in _122){
+var re=new RegExp("\\%\\{"+name+"\\}","g");
+str=str.replace(re,_122[name]);
+}
+if(_123){
+str=str.replace(/%\{([^\}\s]+)\}/g,"");
+}
+return str;
+};
+dojo.string.capitalize=function(str){
+if(!dojo.lang.isString(str)){
+return "";
+}
+if(arguments.length==0){
+str=this;
+}
+var _127=str.split(" ");
+var _128="";
+var len=_127.length;
+for(var i=0;i /gm,">").replace(/"/gm,""");
+if(!_13a){
+str=str.replace(/'/gm,"'");
+}
+return str;
+};
+dojo.string.escapeSql=function(str){
+return str.replace(/'/gm,"''");
+};
+dojo.string.escapeRegExp=function(str){
+return str.replace(/\\/gm,"\\\\").replace(/([\f\b\n\t\r])/gm,"\\$1");
+};
+dojo.string.escapeJavaScript=function(str){
+return str.replace(/(["'\f\b\n\t\r])/gm,"\\$1");
+};
+dojo.string.repeat=function(str,_13f,_140){
+var out="";
+for(var i=0;i<_13f;i++){
+out+=str;
+if(_140&&i<_13f-1){
+out+=_140;
+}
+}
+return out;
+};
+dojo.string.endsWith=function(str,end,_145){
+if(_145){
+str=str.toLowerCase();
+end=end.toLowerCase();
+}
+return str.lastIndexOf(end)==str.length-end.length;
+};
+dojo.string.endsWithAny=function(str){
+for(var i=1;i-1)){
+return true;
+}
+}
+return false;
+};
+dojo.string.pad=function(str,len,c,dir){
+var out=String(str);
+if(!c){
+c="0";
+}
+if(!dir){
+dir=1;
+}
+while(out.length0){
+out=c+out;
+}else{
+out+=c;
+}
+}
+return out;
+};
+dojo.string.padLeft=function(str,len,c){
+return dojo.string.pad(str,len,c,1);
+};
+dojo.string.padRight=function(str,len,c){
+return dojo.string.pad(str,len,c,-1);
+};
+dojo.string.normalizeNewlines=function(text,_15b){
+if(_15b=="\n"){
+text=text.replace(/\r\n/g,"\n");
+text=text.replace(/\r/g,"\n");
+}else{
+if(_15b=="\r"){
+text=text.replace(/\r\n/g,"\r");
+text=text.replace(/\n/g,"\r");
+}else{
+text=text.replace(/([^\r])\n/g,"$1\r\n");
+text=text.replace(/\r([^\n])/g,"\r\n$1");
+}
+}
+return text;
+};
+dojo.string.splitEscaped=function(str,_15d){
+var _15e=[];
+for(var i=0,prevcomma=0;i=4){
+this.changeUrl=_169;
+}
+}
+};
+dojo.lang.extend(dojo.io.Request,{url:"",mimetype:"text/plain",method:"GET",content:undefined,transport:undefined,changeUrl:undefined,formNode:undefined,sync:false,bindSuccess:false,useCache:false,preventCache:false,load:function(type,data,evt){
+},error:function(type,_16e){
+},handle:function(){
+},abort:function(){
+},fromKwArgs:function(_16f){
+if(_16f["url"]){
+_16f.url=_16f.url.toString();
+}
+if(!_16f["method"]&&_16f["formNode"]&&_16f["formNode"].method){
+_16f.method=_16f["formNode"].method;
+}
+if(!_16f["handle"]&&_16f["handler"]){
+_16f.handle=_16f.handler;
+}
+if(!_16f["load"]&&_16f["loaded"]){
+_16f.load=_16f.loaded;
+}
+if(!_16f["changeUrl"]&&_16f["changeURL"]){
+_16f.changeUrl=_16f.changeURL;
+}
+_16f.encoding=dojo.lang.firstValued(_16f["encoding"],djConfig["bindEncoding"],"");
+_16f.sendTransport=dojo.lang.firstValued(_16f["sendTransport"],djConfig["ioSendTransport"],true);
+var _170=dojo.lang.isFunction;
+for(var x=0;x5)&&(_18b[x].indexOf("dojo-")>=0)){
+return "dojo:"+_18b[x].substr(5).toLowerCase();
+}
+}
+}
+}
+}
+return _188.toLowerCase();
+};
+dojo.dom.getUniqueId=function(){
+do{
+var id="dj_unique_"+(++arguments.callee._idIncrement);
+}while(document.getElementById(id));
+return id;
+};
+dojo.dom.getUniqueId._idIncrement=0;
+dojo.dom.firstElement=dojo.dom.getFirstChildElement=function(_18e,_18f){
+var node=_18e.firstChild;
+while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE){
+node=node.nextSibling;
+}
+if(_18f&&node&&node.tagName&&node.tagName.toLowerCase()!=_18f.toLowerCase()){
+node=dojo.dom.nextElement(node,_18f);
+}
+return node;
+};
+dojo.dom.lastElement=dojo.dom.getLastChildElement=function(_191,_192){
+var node=_191.lastChild;
+while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE){
+node=node.previousSibling;
+}
+if(_192&&node&&node.tagName&&node.tagName.toLowerCase()!=_192.toLowerCase()){
+node=dojo.dom.prevElement(node,_192);
+}
+return node;
+};
+dojo.dom.nextElement=dojo.dom.getNextSiblingElement=function(node,_195){
+if(!node){
+return null;
+}
+do{
+node=node.nextSibling;
+}while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE);
+if(node&&_195&&_195.toLowerCase()!=node.tagName.toLowerCase()){
+return dojo.dom.nextElement(node,_195);
+}
+return node;
+};
+dojo.dom.prevElement=dojo.dom.getPreviousSiblingElement=function(node,_197){
+if(!node){
+return null;
+}
+if(_197){
+_197=_197.toLowerCase();
+}
+do{
+node=node.previousSibling;
+}while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE);
+if(node&&_197&&_197.toLowerCase()!=node.tagName.toLowerCase()){
+return dojo.dom.prevElement(node,_197);
+}
+return node;
+};
+dojo.dom.moveChildren=function(_198,_199,trim){
+var _19b=0;
+if(trim){
+while(_198.hasChildNodes()&&_198.firstChild.nodeType==dojo.dom.TEXT_NODE){
+_198.removeChild(_198.firstChild);
+}
+while(_198.hasChildNodes()&&_198.lastChild.nodeType==dojo.dom.TEXT_NODE){
+_198.removeChild(_198.lastChild);
+}
+}
+while(_198.hasChildNodes()){
+_199.appendChild(_198.firstChild);
+_19b++;
+}
+return _19b;
+};
+dojo.dom.copyChildren=function(_19c,_19d,trim){
+var _19f=_19c.cloneNode(true);
+return this.moveChildren(_19f,_19d,trim);
+};
+dojo.dom.removeChildren=function(node){
+var _1a1=node.childNodes.length;
+while(node.hasChildNodes()){
+node.removeChild(node.firstChild);
+}
+return _1a1;
+};
+dojo.dom.replaceChildren=function(node,_1a3){
+dojo.dom.removeChildren(node);
+node.appendChild(_1a3);
+};
+dojo.dom.removeNode=function(node){
+if(node&&node.parentNode){
+return node.parentNode.removeChild(node);
+}
+};
+dojo.dom.getAncestors=function(node,_1a6,_1a7){
+var _1a8=[];
+var _1a9=dojo.lang.isFunction(_1a6);
+while(node){
+if(!_1a9||_1a6(node)){
+_1a8.push(node);
+}
+if(_1a7&&_1a8.length>0){
+return _1a8[0];
+}
+node=node.parentNode;
+}
+if(_1a7){
+return null;
+}
+return _1a8;
+};
+dojo.dom.getAncestorsByTag=function(node,tag,_1ac){
+tag=tag.toLowerCase();
+return dojo.dom.getAncestors(node,function(el){
+return ((el.tagName)&&(el.tagName.toLowerCase()==tag));
+},_1ac);
+};
+dojo.dom.getFirstAncestorByTag=function(node,tag){
+return dojo.dom.getAncestorsByTag(node,tag,true);
+};
+dojo.dom.isDescendantOf=function(node,_1b1,_1b2){
+if(_1b2&&node){
+node=node.parentNode;
+}
+while(node){
+if(node==_1b1){
+return true;
+}
+node=node.parentNode;
+}
+return false;
+};
+dojo.dom.innerXML=function(node){
+if(node.innerXML){
+return node.innerXML;
+}else{
+if(typeof XMLSerializer!="undefined"){
+return (new XMLSerializer()).serializeToString(node);
+}
+}
+};
+dojo.dom.createDocumentFromText=function(str,_1b5){
+if(!_1b5){
+_1b5="text/xml";
+}
+if(typeof DOMParser!="undefined"){
+var _1b6=new DOMParser();
+return _1b6.parseFromString(str,_1b5);
+}else{
+if(typeof ActiveXObject!="undefined"){
+var _1b7=new ActiveXObject("Microsoft.XMLDOM");
+if(_1b7){
+_1b7.async=false;
+_1b7.loadXML(str);
+return _1b7;
+}else{
+dojo.debug("toXml didn't work?");
+}
+}else{
+if(document.createElement){
+var tmp=document.createElement("xml");
+tmp.innerHTML=str;
+if(document.implementation&&document.implementation.createDocument){
+var _1b9=document.implementation.createDocument("foo","",null);
+for(var i=0;i");
+}
+}
+catch(e){
+}
+dojo.io.checkChildrenForFile=function(node){
+var _1d8=false;
+var _1d9=node.getElementsByTagName("input");
+dojo.lang.forEach(_1d9,function(_1da){
+if(_1d8){
+return;
+}
+if(_1da.getAttribute("type")=="file"){
+_1d8=true;
+}
+});
+return _1d8;
+};
+dojo.io.formHasFile=function(_1db){
+return dojo.io.checkChildrenForFile(_1db);
+};
+dojo.io.encodeForm=function(_1dc,_1dd){
+if((!_1dc)||(!_1dc.tagName)||(!_1dc.tagName.toLowerCase()=="form")){
+dojo.raise("Attempted to encode a non-form element.");
+}
+var enc=/utf/i.test(_1dd||"")?encodeURIComponent:dojo.string.encodeAscii;
+var _1df=[];
+for(var i=0;i<_1dc.elements.length;i++){
+var elm=_1dc.elements[i];
+if(elm.disabled||elm.tagName.toLowerCase()=="fieldset"||!elm.name){
+continue;
+}
+var name=enc(elm.name);
+var type=elm.type.toLowerCase();
+if(type=="select-multiple"){
+for(var j=0;j=0){
+while(!this.historyStack[hsl]["urlHash"]){
+hsl--;
+}
+lh=this.historyStack[hsl]["urlHash"];
+}
+if(lh){
+_207=function(){
+if(window.location.hash!=""){
+setTimeout("window.location.href = '"+lh+"';",1);
+}
+_20a();
+};
+}
+this.forwardStack=[];
+var _20d=args["forward"]||args["forwardButton"];
+var tfw=function(){
+if(window.location.hash!=""){
+window.location.href=hash;
+}
+if(_20d){
+_20d();
+}
+};
+if(args["forward"]){
+args.forward=tfw;
+}else{
+if(args["forwardButton"]){
+args.forwardButton=tfw;
+}
+}
+}else{
+if(dojo.render.html.moz){
+if(!this.locationTimer){
+this.locationTimer=setInterval("dojo.io.XMLHTTPTransport.checkLocation();",200);
+}
+}
+}
+}
+this.historyStack.push({"url":url,"callback":_207,"kwArgs":args,"urlHash":hash});
+};
+this.checkLocation=function(){
+var hsl=this.historyStack.length;
+if((window.location.hash==this.initialHash)||(window.location.href==this.initialHref)&&(hsl==1)){
+this.handleBackButton();
+return;
+}
+if(this.forwardStack.length>0){
+if(this.forwardStack[this.forwardStack.length-1].urlHash==window.location.hash){
+this.handleForwardButton();
+return;
+}
+}
+if((hsl>=2)&&(this.historyStack[hsl-2])){
+if(this.historyStack[hsl-2].urlHash==window.location.hash){
+this.handleBackButton();
+return;
+}
+}
+};
+this.iframeLoaded=function(evt,_211){
+var isp=_211.href.split("?");
+if(isp.length<2){
+if(this.historyStack.length==1){
+this.handleBackButton();
+}
+return;
+}
+var _213=isp[1];
+if(this.moveForward){
+this.moveForward=false;
+return;
+}
+var last=this.historyStack.pop();
+if(!last){
+if(this.forwardStack.length>0){
+var next=this.forwardStack[this.forwardStack.length-1];
+if(_213==next.url.split("?")[1]){
+this.handleForwardButton();
+}
+}
+return;
+}
+this.historyStack.push(last);
+if(this.historyStack.length>=2){
+if(isp[1]==this.historyStack[this.historyStack.length-2].url.split("?")[1]){
+this.handleBackButton();
+}
+}else{
+this.handleBackButton();
+}
+};
+this.handleBackButton=function(){
+var last=this.historyStack.pop();
+if(!last){
+return;
+}
+if(last["callback"]){
+last.callback();
+}else{
+if(last.kwArgs["backButton"]){
+last.kwArgs["backButton"]();
+}else{
+if(last.kwArgs["back"]){
+last.kwArgs["back"]();
+}else{
+if(last.kwArgs["handle"]){
+last.kwArgs.handle("back");
+}
+}
+}
+}
+this.forwardStack.push(last);
+};
+this.handleForwardButton=function(){
+var last=this.forwardStack.pop();
+if(!last){
+return;
+}
+if(last.kwArgs["forward"]){
+last.kwArgs.forward();
+}else{
+if(last.kwArgs["forwardButton"]){
+last.kwArgs.forwardButton();
+}else{
+if(last.kwArgs["handle"]){
+last.kwArgs.handle("forward");
+}
+}
+}
+this.historyStack.push(last);
+};
+this.inFlight=[];
+this.inFlightTimer=null;
+this.startWatchingInFlight=function(){
+if(!this.inFlightTimer){
+this.inFlightTimer=setInterval("dojo.io.XMLHTTPTransport.watchInFlight();",10);
+}
+};
+this.watchInFlight=function(){
+for(var x=this.inFlight.length-1;x>=0;x--){
+var tif=this.inFlight[x];
+if(!tif){
+this.inFlight.splice(x,1);
+continue;
+}
+if(4==tif.http.readyState){
+this.inFlight.splice(x,1);
+doLoad(tif.req,tif.http,tif.url,tif.query,tif.useCache);
+if(this.inFlight.length==0){
+clearInterval(this.inFlightTimer);
+this.inFlightTimer=null;
+}
+}
+}
+};
+var _21a=dojo.hostenv.getXmlhttpObject()?true:false;
+this.canHandle=function(_21b){
+return _21a&&dojo.lang.inArray((_21b["mimetype"]||"".toLowerCase()),["text/plain","text/html","application/xml","text/xml","text/javascript","text/json"])&&dojo.lang.inArray(_21b["method"].toLowerCase(),["post","get","head"])&&!(_21b["formNode"]&&dojo.io.formHasFile(_21b["formNode"]));
+};
+this.multipartBoundary="45309FFF-BD65-4d50-99C9-36986896A96F";
+this.bind=function(_21c){
+if(!_21c["url"]){
+if(!_21c["formNode"]&&(_21c["backButton"]||_21c["back"]||_21c["changeUrl"]||_21c["watchForURL"])&&(!djConfig.preventBackButtonFix)){
+this.addToHistory(_21c);
+return true;
+}
+}
+var url=_21c.url;
+var _21e="";
+if(_21c["formNode"]){
+var ta=_21c.formNode.getAttribute("action");
+if((ta)&&(!_21c["url"])){
+url=ta;
+}
+var tp=_21c.formNode.getAttribute("method");
+if((tp)&&(!_21c["method"])){
+_21c.method=tp;
+}
+_21e+=dojo.io.encodeForm(_21c.formNode,_21c.encoding);
+}
+if(url.indexOf("#")>-1){
+dojo.debug("Warning: dojo.io.bind: stripping hash values from url:",url);
+url=url.split("#")[0];
+}
+if(_21c["file"]){
+_21c.method="post";
+}
+if(!_21c["method"]){
+_21c.method="get";
+}
+if(_21c.method.toLowerCase()=="get"){
+_21c.multipart=false;
+}else{
+if(_21c["file"]){
+_21c.multipart=true;
+}else{
+if(!_21c["multipart"]){
+_21c.multipart=false;
+}
+}
+}
+if(_21c["backButton"]||_21c["back"]||_21c["changeUrl"]){
+this.addToHistory(_21c);
+}
+var _221=_21c["content"]||{};
+if(_21c.sendTransport){
+_221["dojo.transport"]="xmlhttp";
+}
+do{
+if(_21c.postContent){
+_21e=_21c.postContent;
+break;
+}
+if(_221){
+_21e+=dojo.io.argsFromMap(_221,_21c.encoding);
+}
+if(_21c.method.toLowerCase()=="get"||!_21c.multipart){
+break;
+}
+var t=[];
+if(_21e.length){
+var q=_21e.split("&");
+for(var i=0;i-1?"&":"?")+_21e;
+}
+if(_228){
+_22d+=(dojo.string.endsWithAny(_22d,"?","&")?"":(_22d.indexOf("?")>-1?"&":"?"))+"dojo.preventCache="+new Date().valueOf();
+}
+http.open(_21c.method.toUpperCase(),_22d,_227);
+setHeaders(http,_21c);
+http.send(null);
+}
+if(!_227){
+doLoad(_21c,http,url,_21e,_229);
+}
+_21c.abort=function(){
+return http.abort();
+};
+return;
+};
+dojo.io.transports.addTransport("XMLHTTPTransport");
+};
+
diff --git a/trunk/core/src/main/resources/org/apache/struts2/static/dojo/dojo.js.uncompressed.js b/trunk/core/src/main/resources/org/apache/struts2/static/dojo/dojo.js.uncompressed.js
new file mode 100644
index 000000000..777d429c0
--- /dev/null
+++ b/trunk/core/src/main/resources/org/apache/struts2/static/dojo/dojo.js.uncompressed.js
@@ -0,0 +1,3476 @@
+/*
+ Copyright (c) 2004-2005, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/**
+* @file bootstrap1.js
+*
+* bootstrap file that runs before hostenv_*.js file.
+*
+* @author Copyright 2004 Mark D. Anderson (mda@discerning.com)
+* @author Licensed under the Academic Free License 2.1 http://www.opensource.org/licenses/afl-2.1.php
+*
+* $Id: bootstrap1.js 2836 2006-01-16 08:36:18Z alex $
+*/
+
+/**
+ * The global djConfig can be set prior to loading the library, to override
+ * certain settings. It does not exist under dojo.* so that it can be set
+ * before the dojo variable exists. Setting any of these variables *after* the
+ * library has loaded does nothing at all. The variables that can be set are
+ * as follows:
+ */
+
+/**
+ * dj_global is an alias for the top-level global object in the host
+ * environment (the "window" object in a browser).
+ */
+var dj_global = this; //typeof window == 'undefined' ? this : window;
+
+function dj_undef(name, obj){
+ if(!obj){ obj = dj_global; }
+ return (typeof obj[name] == "undefined");
+}
+
+if(dj_undef("djConfig")){
+ var djConfig = {};
+}
+
+/**
+ * dojo is the root variable of (almost all) our public symbols.
+ */
+var dojo;
+if(dj_undef("dojo")){ dojo = {}; }
+
+dojo.version = {
+ major: 0, minor: 2, patch: 2, flag: "",
+ revision: Number("$Rev: 2836 $".match(/[0-9]+/)[0]),
+ toString: function() {
+ with (dojo.version) {
+ return major + "." + minor + "." + patch + flag + " (" + revision + ")";
+ }
+ }
+};
+
+/*
+ * evaluate a string like "A.B" without using eval.
+ */
+dojo.evalObjPath = function(objpath, create){
+ // fast path for no periods
+ if(typeof objpath != "string"){ return dj_global; }
+ if(objpath.indexOf('.') == -1){
+ if((dj_undef(objpath, dj_global))&&(create)){
+ dj_global[objpath] = {};
+ }
+ return dj_global[objpath];
+ }
+
+ var syms = objpath.split(/\./);
+ var obj = dj_global;
+ for(var i=0;i 1) {
+ dojo.hostenv.modulesLoadedListeners.push(function() {
+ obj[fcnName]();
+ });
+ }
+};
+
+dojo.hostenv.modulesLoaded = function(){
+ if(this.post_load_){ return; }
+ if((this.loadUriStack.length==0)&&(this.getTextStack.length==0)){
+ if(this.inFlightCount > 0){
+ dojo.debug("files still in flight!");
+ return;
+ }
+ if(typeof setTimeout == "object"){
+ setTimeout("dojo.hostenv.loaded();", 0);
+ }else{
+ dojo.hostenv.loaded();
+ }
+ }
+}
+
+dojo.hostenv.moduleLoaded = function(modulename){
+ var modref = dojo.evalObjPath((modulename.split(".").slice(0, -1)).join('.'));
+ this.loaded_modules_[(new String(modulename)).toLowerCase()] = modref;
+}
+
+/**
+* loadModule("A.B") first checks to see if symbol A.B is defined.
+* If it is, it is simply returned (nothing to do).
+*
+* If it is not defined, it will look for "A/B.js" in the script root directory,
+* followed by "A.js".
+*
+* It throws if it cannot find a file to load, or if the symbol A.B is not
+* defined after loading.
+*
+* It returns the object A.B.
+*
+* This does nothing about importing symbols into the current package.
+* It is presumed that the caller will take care of that. For example, to import
+* all symbols:
+*
+* with (dojo.hostenv.loadModule("A.B")) {
+* ...
+* }
+*
+* And to import just the leaf symbol:
+*
+* var B = dojo.hostenv.loadModule("A.B");
+* ...
+*
+* dj_load is an alias for dojo.hostenv.loadModule
+*/
+dojo.hostenv._global_omit_module_check = false;
+dojo.hostenv.loadModule = function(modulename, exact_only, omit_module_check){
+ if(!modulename){ return; }
+ omit_module_check = this._global_omit_module_check || omit_module_check;
+ var module = this.findModule(modulename, false);
+ if(module){
+ return module;
+ }
+
+ // protect against infinite recursion from mutual dependencies
+ if(dj_undef(modulename, this.loading_modules_)){
+ this.addedToLoadingCount.push(modulename);
+ }
+ this.loading_modules_[modulename] = 1;
+
+ // convert periods to slashes
+ var relpath = modulename.replace(/\./g, '/') + '.js';
+
+ var syms = modulename.split(".");
+ var nsyms = modulename.split(".");
+ for (var i = syms.length - 1; i > 0; i--) {
+ var parentModule = syms.slice(0, i).join(".");
+ var parentModulePath = this.getModulePrefix(parentModule);
+ if (parentModulePath != parentModule) {
+ syms.splice(0, i, parentModulePath);
+ break;
+ }
+ }
+ var last = syms[syms.length - 1];
+ // figure out if we're looking for a full package, if so, we want to do
+ // things slightly diffrently
+ if(last=="*"){
+ modulename = (nsyms.slice(0, -1)).join('.');
+
+ while(syms.length){
+ syms.pop();
+ syms.push(this.pkgFileName);
+ relpath = syms.join("/") + '.js';
+ if(relpath.charAt(0)=="/"){
+ relpath = relpath.slice(1);
+ }
+ ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null));
+ if(ok){ break; }
+ syms.pop();
+ }
+ }else{
+ relpath = syms.join("/") + '.js';
+ modulename = nsyms.join('.');
+ var ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null));
+ if((!ok)&&(!exact_only)){
+ syms.pop();
+ while(syms.length){
+ relpath = syms.join('/') + '.js';
+ ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null));
+ if(ok){ break; }
+ syms.pop();
+ relpath = syms.join('/') + '/'+this.pkgFileName+'.js';
+ if(relpath.charAt(0)=="/"){
+ relpath = relpath.slice(1);
+ }
+ ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null));
+ if(ok){ break; }
+ }
+ }
+
+ if((!ok)&&(!omit_module_check)){
+ dojo.raise("Could not load '" + modulename + "'; last tried '" + relpath + "'");
+ }
+ }
+
+ // check that the symbol was defined
+ if(!omit_module_check){
+ // pass in false so we can give better error
+ module = this.findModule(modulename, false);
+ if(!module){
+ dojo.raise("symbol '" + modulename + "' is not defined after loading '" + relpath + "'");
+ }
+ }
+
+ return module;
+}
+
+/**
+* startPackage("A.B") follows the path, and at each level creates a new empty
+* object or uses what already exists. It returns the result.
+*/
+dojo.hostenv.startPackage = function(packname){
+ var syms = packname.split(/\./);
+ if(syms[syms.length-1]=="*"){
+ syms.pop();
+ }
+ return dojo.evalObjPath(syms.join("."), true);
+}
+
+/**
+ * findModule("A.B") returns the object A.B if it exists, otherwise null.
+ * @param modulename A string like 'A.B'.
+ * @param must_exist Optional, defualt false. throw instead of returning null
+ * if the module does not currently exist.
+ */
+dojo.hostenv.findModule = function(modulename, must_exist) {
+ // check cache
+ /*
+ if(!dj_undef(modulename, this.modules_)){
+ return this.modules_[modulename];
+ }
+ */
+
+ var lmn = (new String(modulename)).toLowerCase();
+
+ if(this.loaded_modules_[lmn]){
+ return this.loaded_modules_[lmn];
+ }
+
+ // see if symbol is defined anyway
+ var module = dojo.evalObjPath(modulename);
+ if((modulename)&&(typeof module != 'undefined')&&(module)){
+ this.loaded_modules_[lmn] = module;
+ return module;
+ }
+
+ if(must_exist){
+ dojo.raise("no loaded module named '" + modulename + "'");
+ }
+ return null;
+}
+
+/**
+* @file hostenv_browser.js
+*
+* Implements the hostenv interface for a browser environment.
+*
+* Perhaps it could be called a "dom" or "useragent" environment.
+*
+* @author Copyright 2004 Mark D. Anderson (mda@discerning.com)
+* @author Licensed under the Academic Free License 2.1 http://www.opensource.org/licenses/afl-2.1.php
+*/
+
+// make jsc shut up (so we can use jsc to sanity check the code even if it will never run it).
+/*@cc_on
+@if (@_jscript_version >= 7)
+var window; var XMLHttpRequest;
+@end
+@*/
+
+if(typeof window == 'undefined'){
+ dojo.raise("no window object");
+}
+
+// attempt to figure out the path to dojo if it isn't set in the config
+(function() {
+ // before we get any further with the config options, try to pick them out
+ // of the URL. Most of this code is from NW
+ if(djConfig.allowQueryConfig){
+ var baseUrl = document.location.toString(); // FIXME: use location.query instead?
+ var params = baseUrl.split("?", 2);
+ if(params.length > 1){
+ var paramStr = params[1];
+ var pairs = paramStr.split("&");
+ for(var x in pairs){
+ var sp = pairs[x].split("=");
+ // FIXME: is this eval dangerous?
+ if((sp[0].length > 9)&&(sp[0].substr(0, 9) == "djConfig.")){
+ var opt = sp[0].substr(9);
+ try{
+ djConfig[opt]=eval(sp[1]);
+ }catch(e){
+ djConfig[opt]=sp[1];
+ }
+ }
+ }
+ }
+ }
+
+ if(((djConfig["baseScriptUri"] == "")||(djConfig["baseRelativePath"] == "")) &&(document && document.getElementsByTagName)){
+ var scripts = document.getElementsByTagName("script");
+ var rePkg = /(__package__|dojo)\.js([\?\.]|$)/i;
+ for(var i = 0; i < scripts.length; i++) {
+ var src = scripts[i].getAttribute("src");
+ if(!src) { continue; }
+ var m = src.match(rePkg);
+ if(m) {
+ root = src.substring(0, m.index);
+ if(!this["djConfig"]) { djConfig = {}; }
+ if(djConfig["baseScriptUri"] == "") { djConfig["baseScriptUri"] = root; }
+ if(djConfig["baseRelativePath"] == "") { djConfig["baseRelativePath"] = root; }
+ break;
+ }
+ }
+ }
+
+ var dr = dojo.render;
+ var drh = dojo.render.html;
+ var dua = drh.UA = navigator.userAgent;
+ var dav = drh.AV = navigator.appVersion;
+ var t = true;
+ var f = false;
+ drh.capable = t;
+ drh.support.builtin = t;
+
+ dr.ver = parseFloat(drh.AV);
+ dr.os.mac = dav.indexOf("Macintosh") >= 0;
+ dr.os.win = dav.indexOf("Windows") >= 0;
+ // could also be Solaris or something, but it's the same browser
+ dr.os.linux = dav.indexOf("X11") >= 0;
+
+ drh.opera = dua.indexOf("Opera") >= 0;
+ drh.khtml = (dav.indexOf("Konqueror") >= 0)||(dav.indexOf("Safari") >= 0);
+ drh.safari = dav.indexOf("Safari") >= 0;
+ var geckoPos = dua.indexOf("Gecko");
+ drh.mozilla = drh.moz = (geckoPos >= 0)&&(!drh.khtml);
+ if (drh.mozilla) {
+ // gecko version is YYYYMMDD
+ drh.geckoVersion = dua.substring(geckoPos + 6, geckoPos + 14);
+ }
+ drh.ie = (document.all)&&(!drh.opera);
+ drh.ie50 = drh.ie && dav.indexOf("MSIE 5.0")>=0;
+ drh.ie55 = drh.ie && dav.indexOf("MSIE 5.5")>=0;
+ drh.ie60 = drh.ie && dav.indexOf("MSIE 6.0")>=0;
+
+ dr.vml.capable=drh.ie;
+ dr.svg.capable = f;
+ dr.svg.support.plugin = f;
+ dr.svg.support.builtin = f;
+ dr.svg.adobe = f;
+ if (document.implementation
+ && document.implementation.hasFeature
+ && document.implementation.hasFeature("org.w3c.dom.svg", "1.0")
+ ){
+ dr.svg.capable = t;
+ dr.svg.support.builtin = t;
+ dr.svg.support.plugin = f;
+ dr.svg.adobe = f;
+ }else{
+ // check for ASVG
+ if(navigator.mimeTypes && navigator.mimeTypes.length > 0){
+ var result = navigator.mimeTypes["image/svg+xml"] ||
+ navigator.mimeTypes["image/svg"] ||
+ navigator.mimeTypes["image/svg-xml"];
+ if (result){
+ dr.svg.adobe = result && result.enabledPlugin &&
+ result.enabledPlugin.description &&
+ (result.enabledPlugin.description.indexOf("Adobe") > -1);
+ if(dr.svg.adobe) {
+ dr.svg.capable = t;
+ dr.svg.support.plugin = t;
+ }
+ }
+ }else if(drh.ie && dr.os.win){
+ var result = f;
+ try {
+ var test = new ActiveXObject("Adobe.SVGCtl");
+ result = t;
+ } catch(e){}
+ if (result){
+ dr.svg.capable = t;
+ dr.svg.support.plugin = t;
+ dr.svg.adobe = t;
+ }
+ }else{
+ dr.svg.capable = f;
+ dr.svg.support.plugin = f;
+ dr.svg.adobe = f;
+ }
+ }
+})();
+
+dojo.hostenv.startPackage("dojo.hostenv");
+
+dojo.hostenv.name_ = 'browser';
+dojo.hostenv.searchIds = [];
+
+// These are in order of decreasing likelihood; this will change in time.
+var DJ_XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
+
+dojo.hostenv.getXmlhttpObject = function(){
+ var http = null;
+ var last_e = null;
+ try{ http = new XMLHttpRequest(); }catch(e){}
+ if(!http){
+ for(var i=0; i<3; ++i){
+ var progid = DJ_XMLHTTP_PROGIDS[i];
+ try{
+ http = new ActiveXObject(progid);
+ }catch(e){
+ last_e = e;
+ }
+
+ if(http){
+ DJ_XMLHTTP_PROGIDS = [progid]; // so faster next time
+ break;
+ }
+ }
+
+ /*if(http && !http.toString) {
+ http.toString = function() { "[object XMLHttpRequest]"; }
+ }*/
+ }
+
+ if(!http){
+ return dojo.raise("XMLHTTP not available", last_e);
+ }
+
+ return http;
+}
+
+/**
+ * Read the contents of the specified uri and return those contents.
+ *
+ * @param uri A relative or absolute uri. If absolute, it still must be in the
+ * same "domain" as we are.
+ *
+ * @param async_cb If not specified, load synchronously. If specified, load
+ * asynchronously, and use async_cb as the progress handler which takes the
+ * xmlhttp object as its argument. If async_cb, this function returns null.
+ *
+ * @param fail_ok Default false. If fail_ok and !async_cb and loading fails,
+ * return null instead of throwing.
+ */
+dojo.hostenv.getText = function(uri, async_cb, fail_ok){
+
+ var http = this.getXmlhttpObject();
+
+ if(async_cb){
+ http.onreadystatechange = function(){
+ if((4==http.readyState)&&(http["status"])){
+ if(http.status==200){
+ // dojo.debug("LOADED URI: "+uri);
+ async_cb(http.responseText);
+ }
+ }
+ }
+ }
+
+ http.open('GET', uri, async_cb ? true : false);
+ http.send(null);
+ if(async_cb){
+ return null;
+ }
+
+ return http.responseText;
+}
+
+/*
+ * It turns out that if we check *right now*, as this script file is being loaded,
+ * then the last script element in the window DOM is ourselves.
+ * That is because any subsequent script elements haven't shown up in the document
+ * object yet.
+ */
+ /*
+function dj_last_script_src() {
+ var scripts = window.document.getElementsByTagName('script');
+ if(scripts.length < 1){
+ dojo.raise("No script elements in window.document, so can't figure out my script src");
+ }
+ var script = scripts[scripts.length - 1];
+ var src = script.src;
+ if(!src){
+ dojo.raise("Last script element (out of " + scripts.length + ") has no src");
+ }
+ return src;
+}
+
+if(!dojo.hostenv["library_script_uri_"]){
+ dojo.hostenv.library_script_uri_ = dj_last_script_src();
+}
+*/
+
+dojo.hostenv.defaultDebugContainerId = 'dojoDebug';
+dojo.hostenv._println_buffer = [];
+dojo.hostenv._println_safe = false;
+dojo.hostenv.println = function (line){
+ if(!dojo.hostenv._println_safe){
+ dojo.hostenv._println_buffer.push(line);
+ }else{
+ try {
+ var console = document.getElementById(djConfig.debugContainerId ?
+ djConfig.debugContainerId : dojo.hostenv.defaultDebugContainerId);
+ if(!console) { console = document.getElementsByTagName("body")[0] || document.body; }
+
+ var div = document.createElement("div");
+ div.appendChild(document.createTextNode(line));
+ console.appendChild(div);
+ } catch (e) {
+ try{
+ // safari needs the output wrapped in an element for some reason
+ document.write("" + line + "
");
+ }catch(e2){
+ window.status = line;
+ }
+ }
+ }
+}
+
+dojo.addOnLoad(function(){
+ dojo.hostenv._println_safe = true;
+ while(dojo.hostenv._println_buffer.length > 0){
+ dojo.hostenv.println(dojo.hostenv._println_buffer.shift());
+ }
+});
+
+function dj_addNodeEvtHdlr (node, evtName, fp, capture){
+ var oldHandler = node["on"+evtName] || function(){};
+ node["on"+evtName] = function(){
+ fp.apply(node, arguments);
+ oldHandler.apply(node, arguments);
+ }
+ return true;
+}
+
+dj_addNodeEvtHdlr(window, "load", function(){
+ if(dojo.render.html.ie){
+ dojo.hostenv.makeWidgets();
+ }
+ dojo.hostenv.modulesLoaded();
+});
+
+dojo.hostenv.makeWidgets = function(){
+ // you can put searchIds in djConfig and dojo.hostenv at the moment
+ // we should probably eventually move to one or the other
+ var sids = [];
+ if(djConfig.searchIds && djConfig.searchIds.length > 0) {
+ sids = sids.concat(djConfig.searchIds);
+ }
+ if(dojo.hostenv.searchIds && dojo.hostenv.searchIds.length > 0) {
+ sids = sids.concat(dojo.hostenv.searchIds);
+ }
+
+ if((djConfig.parseWidgets)||(sids.length > 0)){
+ if(dojo.evalObjPath("dojo.widget.Parse")){
+ // we must do this on a delay to avoid:
+ // http://www.shaftek.org/blog/archives/000212.html
+ // IE is such a tremendous peice of shit.
+ try{
+ var parser = new dojo.xml.Parse();
+ if(sids.length > 0){
+ for(var x=0; xv\:*{ behavior:url(#default#VML); }');
+ document.write(' ');
+ }
+} catch (e) { }
+
+// stub, over-ridden by debugging code. This will at least keep us from
+// breaking when it's not included
+dojo.hostenv.writeIncludes = function(){}
+
+dojo.hostenv.byId = dojo.byId = function(id, doc){
+ if(typeof id == "string" || id instanceof String){
+ if(!doc){ doc = document; }
+ return doc.getElementById(id);
+ }
+ return id; // assume it's a node
+}
+
+dojo.hostenv.byIdArray = dojo.byIdArray = function(){
+ var ids = [];
+ for(var i = 0; i < arguments.length; i++){
+ if((arguments[i] instanceof Array)||(typeof arguments[i] == "array")){
+ for(var j = 0; j < arguments[i].length; j++){
+ ids = ids.concat(dojo.hostenv.byIdArray(arguments[i][j]));
+ }
+ }else{
+ ids.push(dojo.hostenv.byId(arguments[i]));
+ }
+ }
+ return ids;
+}
+
+/*
+ * bootstrap2.js - runs after the hostenv_*.js file.
+ */
+
+/*
+ * This method taks a "map" of arrays which one can use to optionally load dojo
+ * modules. The map is indexed by the possible dojo.hostenv.name_ values, with
+ * two additional values: "default" and "common". The items in the "default"
+ * array will be loaded if none of the other items have been choosen based on
+ * the hostenv.name_ item. The items in the "common" array will _always_ be
+ * loaded, regardless of which list is chosen. Here's how it's normally
+ * called:
+ *
+ * dojo.hostenv.conditionalLoadModule({
+ * browser: [
+ * ["foo.bar.baz", true, true], // an example that passes multiple args to loadModule()
+ * "foo.sample.*",
+ * "foo.test,
+ * ],
+ * default: [ "foo.sample.*" ],
+ * common: [ "really.important.module.*" ]
+ * });
+ */
+dojo.hostenv.conditionalLoadModule = function(modMap){
+ var common = modMap["common"]||[];
+ var result = (modMap[dojo.hostenv.name_]) ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]);
+
+ for(var x=0; x= 0; i--) {
+ if(arr[i] === val){ return i; }
+ }
+ }else{
+ for(var i = arr.length-1; i >= 0; i--) {
+ if(arr[i] == val){ return i; }
+ }
+ }
+ return -1;
+}
+
+dojo.lang.lastIndexOf = dojo.lang.findLast;
+
+dojo.lang.inArray = function(arr, val){
+ return dojo.lang.find(arr, val) > -1;
+}
+
+dojo.lang.getNameInObj = function(ns, item){
+ if(!ns){ ns = dj_global; }
+
+ for(var x in ns){
+ if(ns[x] === item){
+ return new String(x);
+ }
+ }
+ return null;
+}
+
+// FIXME: Is this worthless since you can do: if(name in obj)
+// is this the right place for this?
+dojo.lang.has = function(obj, name){
+ return (typeof obj[name] !== 'undefined');
+}
+
+dojo.lang.isEmpty = function(obj) {
+ if(dojo.lang.isObject(obj)) {
+ var tmp = {};
+ var count = 0;
+ for(var x in obj){
+ if(obj[x] && (!tmp[x])){
+ count++;
+ break;
+ }
+ }
+ return (count == 0);
+ } else if(dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)) {
+ return obj.length == 0;
+ }
+}
+
+dojo.lang.forEach = function(arr, unary_func, fix_length){
+ var isString = dojo.lang.isString(arr);
+ if(isString) { arr = arr.split(""); }
+ var il = arr.length;
+ for(var i=0; i< ((fix_length) ? il : arr.length); i++){
+ if(unary_func(arr[i], i, arr) == "break"){
+ break;
+ }
+ }
+}
+
+dojo.lang.map = function(arr, obj, unary_func){
+ var isString = dojo.lang.isString(arr);
+ if(isString){
+ arr = arr.split("");
+ }
+ if(dojo.lang.isFunction(obj)&&(!unary_func)){
+ unary_func = obj;
+ obj = dj_global;
+ }else if(dojo.lang.isFunction(obj) && unary_func){
+ // ff 1.5 compat
+ var tmpObj = obj;
+ obj = unary_func;
+ unary_func = tmpObj;
+ }
+
+ if(Array.map){
+ var outArr = Array.map(arr, unary_func, obj);
+ }else{
+ var outArr = [];
+ for(var i=0;i= 3) { dojo.raise("thisObject doesn't exist!"); }
+ thisObject = dj_global;
+ }
+
+ for(var i = 0; i < arr.length; i++) {
+ if(!callback.call(thisObject, arr[i], i, arr)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+dojo.lang.some = function(arr, callback, thisObject) {
+ var isString = dojo.lang.isString(arr);
+ if(isString) { arr = arr.split(""); }
+ if(Array.some) {
+ return Array.some(arr, callback, thisObject);
+ } else {
+ if(!thisObject) {
+ if(arguments.length >= 3) { dojo.raise("thisObject doesn't exist!"); }
+ thisObject = dj_global;
+ }
+
+ for(var i = 0; i < arr.length; i++) {
+ if(callback.call(thisObject, arr[i], i, arr)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+dojo.lang.filter = function(arr, callback, thisObject) {
+ var isString = dojo.lang.isString(arr);
+ if(isString) { arr = arr.split(""); }
+ if(Array.filter) {
+ var outArr = Array.filter(arr, callback, thisObject);
+ } else {
+ if(!thisObject) {
+ if(arguments.length >= 3) { dojo.raise("thisObject doesn't exist!"); }
+ thisObject = dj_global;
+ }
+
+ var outArr = [];
+ for(var i = 0; i < arr.length; i++) {
+ if(callback.call(thisObject, arr[i], i, arr)) {
+ outArr.push(arr[i]);
+ }
+ }
+ }
+ if(isString) {
+ return outArr.join("");
+ } else {
+ return outArr;
+ }
+}
+
+dojo.AdapterRegistry = function(){
+ /***
+ A registry to facilitate adaptation.
+
+ Pairs is an array of [name, check, wrap] triples
+
+ All check/wrap functions in this registry should be of the same arity.
+ ***/
+ this.pairs = [];
+}
+
+dojo.lang.extend(dojo.AdapterRegistry, {
+ register: function (name, check, wrap, /* optional */ override){
+ /***
+ The check function should return true if the given arguments are
+ appropriate for the wrap function.
+
+ If override is given and true, the check function will be given
+ highest priority. Otherwise, it will be the lowest priority
+ adapter.
+ ***/
+
+ if (override) {
+ this.pairs.unshift([name, check, wrap]);
+ } else {
+ this.pairs.push([name, check, wrap]);
+ }
+ },
+
+ match: function (/* ... */) {
+ /***
+ Find an adapter for the given arguments.
+
+ If no suitable adapter is found, throws NotFound.
+ ***/
+ for(var i = 0; i < this.pairs.length; i++){
+ var pair = this.pairs[i];
+ if(pair[1].apply(this, arguments)){
+ return pair[2].apply(this, arguments);
+ }
+ }
+ throw new Error("No match found");
+ // dojo.raise("No match found");
+ },
+
+ unregister: function (name) {
+ /***
+ Remove a named adapter from the registry
+ ***/
+ for(var i = 0; i < this.pairs.length; i++){
+ var pair = this.pairs[i];
+ if(pair[0] == name){
+ this.pairs.splice(i, 1);
+ return true;
+ }
+ }
+ return false;
+ }
+});
+
+dojo.lang.reprRegistry = new dojo.AdapterRegistry();
+dojo.lang.registerRepr = function(name, check, wrap, /*optional*/ override){
+ /***
+ Register a repr function. repr functions should take
+ one argument and return a string representation of it
+ suitable for developers, primarily used when debugging.
+
+ If override is given, it is used as the highest priority
+ repr, otherwise it will be used as the lowest.
+ ***/
+ dojo.lang.reprRegistry.register(name, check, wrap, override);
+ };
+
+dojo.lang.repr = function(obj){
+ /***
+ Return a "programmer representation" for an object
+ ***/
+ if(typeof(obj) == "undefined"){
+ return "undefined";
+ }else if(obj === null){
+ return "null";
+ }
+
+ try{
+ if(typeof(obj["__repr__"]) == 'function'){
+ return obj["__repr__"]();
+ }else if((typeof(obj["repr"]) == 'function')&&(obj.repr != arguments.callee)){
+ return obj["repr"]();
+ }
+ return dojo.lang.reprRegistry.match(obj);
+ }catch(e){
+ if(typeof(obj.NAME) == 'string' && (
+ obj.toString == Function.prototype.toString ||
+ obj.toString == Object.prototype.toString
+ )){
+ return o.NAME;
+ }
+ }
+
+ if(typeof(obj) == "function"){
+ obj = (obj + "").replace(/^\s+/, "");
+ var idx = obj.indexOf("{");
+ if(idx != -1){
+ obj = obj.substr(0, idx) + "{...}";
+ }
+ }
+ return obj + "";
+}
+
+dojo.lang.reprArrayLike = function(arr){
+ try{
+ var na = dojo.lang.map(arr, dojo.lang.repr);
+ return "[" + na.join(", ") + "]";
+ }catch(e){ }
+};
+
+dojo.lang.reprString = function(str){
+ return ('"' + str.replace(/(["\\])/g, '\\$1') + '"'
+ ).replace(/[\f]/g, "\\f"
+ ).replace(/[\b]/g, "\\b"
+ ).replace(/[\n]/g, "\\n"
+ ).replace(/[\t]/g, "\\t"
+ ).replace(/[\r]/g, "\\r");
+};
+
+dojo.lang.reprNumber = function(num){
+ return num + "";
+};
+
+(function(){
+ var m = dojo.lang;
+ m.registerRepr("arrayLike", m.isArrayLike, m.reprArrayLike);
+ m.registerRepr("string", m.isString, m.reprString);
+ m.registerRepr("numbers", m.isNumber, m.reprNumber);
+ m.registerRepr("boolean", m.isBoolean, m.reprNumber);
+ // m.registerRepr("numbers", m.typeMatcher("number", "boolean"), m.reprNumber);
+})();
+
+/**
+ * Creates a 1-D array out of all the arguments passed,
+ * unravelling any array-like objects in the process
+ *
+ * Ex:
+ * unnest(1, 2, 3) ==> [1, 2, 3]
+ * unnest(1, [2, [3], [[[4]]]]) ==> [1, 2, 3, 4]
+ */
+dojo.lang.unnest = function(/* ... */) {
+ var out = [];
+ for(var i = 0; i < arguments.length; i++) {
+ if(dojo.lang.isArrayLike(arguments[i])) {
+ var add = dojo.lang.unnest.apply(this, arguments[i]);
+ out = out.concat(add);
+ } else {
+ out.push(arguments[i]);
+ }
+ }
+ return out;
+}
+
+/**
+ * Return the first argument that isn't undefined
+ */
+dojo.lang.firstValued = function(/* ... */) {
+ for(var i = 0; i < arguments.length; i++) {
+ if(typeof arguments[i] != "undefined") {
+ return arguments[i];
+ }
+ }
+ return undefined;
+}
+
+/**
+ * Converts an array-like object (i.e. arguments, DOMCollection)
+ * to an array
+**/
+dojo.lang.toArray = function(arrayLike, startOffset) {
+ var array = [];
+ for(var i = startOffset||0; i < arrayLike.length; i++) {
+ array.push(arrayLike[i]);
+ }
+ return array;
+}
+
+dojo.provide("dojo.string");
+dojo.require("dojo.lang");
+
+/**
+ * Trim whitespace from 'str'. If 'wh' > 0,
+ * only trim from start, if 'wh' < 0, only trim
+ * from end, otherwise trim both ends
+ */
+dojo.string.trim = function(str, wh){
+ if(!dojo.lang.isString(str)){ return str; }
+ if(!str.length){ return str; }
+ if(wh > 0) {
+ return str.replace(/^\s+/, "");
+ } else if(wh < 0) {
+ return str.replace(/\s+$/, "");
+ } else {
+ return str.replace(/^\s+|\s+$/g, "");
+ }
+}
+
+/**
+ * Trim whitespace at the beginning of 'str'
+ */
+dojo.string.trimStart = function(str) {
+ return dojo.string.trim(str, 1);
+}
+
+/**
+ * Trim whitespace at the end of 'str'
+ */
+dojo.string.trimEnd = function(str) {
+ return dojo.string.trim(str, -1);
+}
+
+/**
+ * Parameterized string function
+ * str - formatted string with %{values} to be replaces
+ * pairs - object of name: "value" value pairs
+ * killExtra - remove all remaining %{values} after pairs are inserted
+ */
+dojo.string.paramString = function(str, pairs, killExtra) {
+ for(var name in pairs) {
+ var re = new RegExp("\\%\\{" + name + "\\}", "g");
+ str = str.replace(re, pairs[name]);
+ }
+
+ if(killExtra) { str = str.replace(/%\{([^\}\s]+)\}/g, ""); }
+ return str;
+}
+
+/** Uppercases the first letter of each word */
+dojo.string.capitalize = function (str) {
+ if (!dojo.lang.isString(str)) { return ""; }
+ if (arguments.length == 0) { str = this; }
+ var words = str.split(' ');
+ var retval = "";
+ var len = words.length;
+ for (var i=0; i /gm, ">").replace(/"/gm, """);
+ if(!noSingleQuotes) { str = str.replace(/'/gm, "'"); }
+ return str;
+}
+
+dojo.string.escapeSql = function(str) {
+ return str.replace(/'/gm, "''");
+}
+
+dojo.string.escapeRegExp = function(str) {
+ return str.replace(/\\/gm, "\\\\").replace(/([\f\b\n\t\r])/gm, "\\$1");
+}
+
+dojo.string.escapeJavaScript = function(str) {
+ return str.replace(/(["'\f\b\n\t\r])/gm, "\\$1");
+}
+
+/**
+ * Return 'str' repeated 'count' times, optionally
+ * placing 'separator' between each rep
+ */
+dojo.string.repeat = function(str, count, separator) {
+ var out = "";
+ for(var i = 0; i < count; i++) {
+ out += str;
+ if(separator && i < count - 1) {
+ out += separator;
+ }
+ }
+ return out;
+}
+
+/**
+ * Returns true if 'str' ends with 'end'
+ */
+dojo.string.endsWith = function(str, end, ignoreCase) {
+ if(ignoreCase) {
+ str = str.toLowerCase();
+ end = end.toLowerCase();
+ }
+ return str.lastIndexOf(end) == str.length - end.length;
+}
+
+/**
+ * Returns true if 'str' ends with any of the arguments[2 -> n]
+ */
+dojo.string.endsWithAny = function(str /* , ... */) {
+ for(var i = 1; i < arguments.length; i++) {
+ if(dojo.string.endsWith(str, arguments[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns true if 'str' starts with 'start'
+ */
+dojo.string.startsWith = function(str, start, ignoreCase) {
+ if(ignoreCase) {
+ str = str.toLowerCase();
+ start = start.toLowerCase();
+ }
+ return str.indexOf(start) == 0;
+}
+
+/**
+ * Returns true if 'str' starts with any of the arguments[2 -> n]
+ */
+dojo.string.startsWithAny = function(str /* , ... */) {
+ for(var i = 1; i < arguments.length; i++) {
+ if(dojo.string.startsWith(str, arguments[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns true if 'str' starts with any of the arguments 2 -> n
+ */
+dojo.string.has = function(str /* , ... */) {
+ for(var i = 1; i < arguments.length; i++) {
+ if(str.indexOf(arguments[i] > -1)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Pad 'str' to guarantee that it is at least 'len' length
+ * with the character 'c' at either the start (dir=1) or
+ * end (dir=-1) of the string
+ */
+dojo.string.pad = function(str, len/*=2*/, c/*='0'*/, dir/*=1*/) {
+ var out = String(str);
+ if(!c) {
+ c = '0';
+ }
+ if(!dir) {
+ dir = 1;
+ }
+ while(out.length < len) {
+ if(dir > 0) {
+ out = c + out;
+ } else {
+ out += c;
+ }
+ }
+ return out;
+}
+
+/** same as dojo.string.pad(str, len, c, 1) */
+dojo.string.padLeft = function(str, len, c) {
+ return dojo.string.pad(str, len, c, 1);
+}
+
+/** same as dojo.string.pad(str, len, c, -1) */
+dojo.string.padRight = function(str, len, c) {
+ return dojo.string.pad(str, len, c, -1);
+}
+
+dojo.string.normalizeNewlines = function (text,newlineChar) {
+ if (newlineChar == "\n") {
+ text = text.replace(/\r\n/g, "\n");
+ text = text.replace(/\r/g, "\n");
+ } else if (newlineChar == "\r") {
+ text = text.replace(/\r\n/g, "\r");
+ text = text.replace(/\n/g, "\r");
+ } else {
+ text = text.replace(/([^\r])\n/g, "$1\r\n");
+ text = text.replace(/\r([^\n])/g, "\r\n$1");
+ }
+ return text;
+}
+
+dojo.string.splitEscaped = function (str,charac) {
+ var components = [];
+ for (var i = 0, prevcomma = 0; i < str.length; i++) {
+ if (str.charAt(i) == '\\') { i++; continue; }
+ if (str.charAt(i) == charac) {
+ components.push(str.substring(prevcomma, i));
+ prevcomma = i + 1;
+ }
+ }
+ components.push(str.substr(prevcomma));
+ return components;
+}
+
+
+// do we even want to offer this? is it worth it?
+dojo.string.addToPrototype = function() {
+ for(var method in dojo.string) {
+ if(dojo.lang.isFunction(dojo.string[method])) {
+ var func = (function() {
+ var meth = method;
+ switch(meth) {
+ case "addToPrototype":
+ return null;
+ break;
+ case "escape":
+ return function(type) {
+ return dojo.string.escape(type, this);
+ }
+ break;
+ default:
+ return function() {
+ var args = [this];
+ for(var i = 0; i < arguments.length; i++) {
+ args.push(arguments[i]);
+ }
+ dojo.debug(args);
+ return dojo.string[meth].apply(dojo.string, args);
+ }
+ }
+ })();
+ if(func) { String.prototype[method] = func; }
+ }
+ }
+}
+
+dojo.provide("dojo.io.IO");
+dojo.require("dojo.string");
+
+/******************************************************************************
+ * Notes about dojo.io design:
+ *
+ * The dojo.io.* package has the unenviable task of making a lot of different
+ * types of I/O feel natural, despite a universal lack of good (or even
+ * reasonable!) I/O capability in the host environment. So lets pin this down
+ * a little bit further.
+ *
+ * Rhino:
+ * perhaps the best situation anywhere. Access to Java classes allows you
+ * to do anything one might want in terms of I/O, both synchronously and
+ * async. Can open TCP sockets and perform low-latency client/server
+ * interactions. HTTP transport is available through Java HTTP client and
+ * server classes. Wish it were always this easy.
+ *
+ * xpcshell:
+ * XPCOM for I/O. A cluster-fuck to be sure.
+ *
+ * spidermonkey:
+ * S.O.L.
+ *
+ * Browsers:
+ * Browsers generally do not provide any useable filesystem access. We are
+ * therefore limited to HTTP for moving information to and from Dojo
+ * instances living in a browser.
+ *
+ * XMLHTTP:
+ * Sync or async, allows reading of arbitrary text files (including
+ * JS, which can then be eval()'d), writing requires server
+ * cooperation and is limited to HTTP mechanisms (POST and GET).
+ *
+ *