0

I currently have a multi-language site based on Struts 2. Unfortunately, JSP leaves traces of white spaces everywhere in between struts tags. This is perfectly fine if the site is in English language (where there are spaces in between words) but not so if it's in say, Traditional Chinese.

I'd like to trim all the whitespaces in the JSP page only if the user has selected Traditional Chinese as his/her preferred language.

I've tried the following code, but it trims the whitespaces even when the page is in English.

<s:if test="#session.languageId == 2"> // languageId = 2 (Chinese), 1 (English)
    <fmt:setLocale value="zh" />
    <%@ page trimDirectiveWhitespaces="true" %>
</s:if>

I'm guessing the above code breaks because of something to do with request time versus runtime code.

Is there any way to achieve the above objective?

Brenda Nicole Tan
  • 5,164
  • 2
  • 17
  • 17

1 Answers1

1

Problem

You are completely correct in the compile-time versus request-time (i.e. runtime) distinction. The reason your code

<s:if test="#session.languageId == 2"> // languageId = 2 (Chinese), 1 (English)
    <fmt:setLocale value="zh" />
    <%@ page trimDirectiveWhitespaces="true" %>
</s:if>

does not work is because the <%@ page %> is a compile-time directive which configures how the JSP gets translated into a servlet class, whereas the <s:if> tag is a request-time JSP tag. And since a JSP must be compiled/translated into a servlet before it can serve a request, it is impossible to set the <%@ page %> directive at request-time.

Solution

Because the <%@ page %> directive is compile-time, and you want to serve one of two different page contents (with trimDirectiveWhitespaces set to true and false, respectively), you need to have two identical JSP files, except one with trimDirectiveWhitespaces true and the other one false. Let's call the two JSP files trim.jsp and notrim.jsp.

And rather than keeping the same content in trim.jsp and notrim.jsp, you can instead put it into a common JSPF file which the two JSP files statically include using <%@ include %>. Let's call this common file common.jspf.

At this point if you're following the example below, you should be able to point your browser at trim.jsp (and notrim.jsp) to see the whitespace-trimmed (and untrimmed) version of common.jspf.

As a final step, you need to introduce your final, client-visible JSP file, which we'll call index.jsp. index.jsp will dynamically include either trim.jsp or notrim.jsp based on some request-time condition. In your case it's the Struts expression #session.languageId == 2, but in the example it's the EL expression ${param.languageId == 2}.

Now you can point your browser at index.jsp and see the "English version" (no whitespace trimmed) or go to index.jsp?languageId=2 and see the "Chinese version" (whitespace trimmed).

Example

The files in this example illustrate the Solution outlined above, but they use JSTL tags instead of Struts tags to keep it generic.

common.jspf

Contents which you want to be whitespace-trimmed or not based on the locale.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="myvar" value="myvalue" />
<c:out value="${myvar}" />

trim.jsp

A representation of common.jspf with directive whitespace trimmed.

<%@ page trimDirectiveWhitespaces="true"
 %><%@ include file="./common.jspf" %>

notrim.jsp

A representation of common.jspf with directive whitespace not trimmed.

<%@ page trimDirectiveWhitespaces="false"
 %><%@ include file="./common.jspf" %>

index.jsp

Client-visible JSP page which decides whether to serve trim.jsp or notrim.jsp at request time.

<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%-- languageId = 2 (Chinese), not 2 (English) --%>
<jsp:include page="${param.languageId == 2 ? './include_nows.jsp' : './include_ws.jsp'}" />

Miscellany

  1. common.jspf is a JSPF instead of a JSP because its only intention is to be included from a JSP file and we don't want it acessible directly. (See: What is .jspf file extension? How to compile it?)

  2. trim.jsp and notrim.jsp use <%@ include %> (static, or compile-time include) instead of <jsp:include> (dynamic, or request-time include) to include common.jspf since we want them to behave as if the contents of common.jspf appeared directly where the include happens. (See: What's the difference between including files with JSP include directive, JSP include action and using JSP Tag Files?)

  3. trim.jsp and notrim.jsp intentionally put the terminator %> on the next line to not introduce whitespace before or after common.jspf. This is normally done with trimDirectiveWhitespace but that's not an option here since it will affect common.jspf as well.

  4. trim.jsp and notrim.jsp MUST be JSP files and not JSPF files, because they are to be included dynamically with <jsp:include>. However, they too, are not supposed to be accessed directly by the client. If your keep your JSP files under <webapp-root>/WEB-INF then this is not a problem. Otherwise, you may want to deny direct access to them. (See: Denying direct access to jsp pages)

  5. index.jsp uses <jsp:include> to include trim.jsp (or notrim.jsp) instead of <%@ include %> because the latter is a compile-time include which, as explained in Solution, cannot be used applied conditionally at request time.

  6. index.jsp uses <%@ page trimDirectiveWhitespace="true" %> instead of the method described in (3) for readability and because it does not interfere with any whitespace trimming behavior in included files. (Because it uses a dynamic include and the included files have their own whitespace trimming behavior.)

Community
  • 1
  • 1
mxxk
  • 9,514
  • 5
  • 38
  • 46