You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2009/07/31 20:12:51 UTC
svn commit: r799681 [6/24] - in /struts/sandbox/trunk/struts2-jsp-plugin: ./
src/main/java/org/apache/struts/ src/main/java/org/apache/struts2/
src/main/java/org/apache/struts2/compiler/
src/main/java/org/apache/struts2/jasper/ src/main/java/org/apache...
Added: struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspReader.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspReader.java?rev=799681&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspReader.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspReader.java Fri Jul 31 18:12:48 2009
@@ -0,0 +1,657 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.struts2.jasper.compiler;
+
+import java.io.CharArrayWriter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Vector;
+import java.util.jar.JarFile;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.jasper.JasperException;
+import org.apache.struts2.jasper.JspCompilationContext;
+
+/**
+ * JspReader is an input buffer for the JSP parser. It should allow
+ * unlimited lookahead and pushback. It also has a bunch of parsing
+ * utility methods for understanding htmlesque thingies.
+ *
+ * @author Anil K. Vijendran
+ * @author Anselm Baird-Smith
+ * @author Harish Prabandham
+ * @author Rajiv Mordani
+ * @author Mandar Raje
+ * @author Danno Ferrin
+ * @author Kin-man Chung
+ * @author Shawn Bayern
+ * @author Mark Roth
+ */
+
+class JspReader {
+
+ /**
+ * Logger.
+ */
+ private Log log = LogFactory.getLog(JspReader.class);
+
+ /**
+ * The current spot in the file.
+ */
+ private Mark current;
+
+ /**
+ * What is this?
+ */
+ private String master;
+
+ /**
+ * The list of source files.
+ */
+ private List sourceFiles;
+
+ /**
+ * The current file ID (-1 indicates an error or no file).
+ */
+ private int currFileId;
+
+ /**
+ * Seems redundant.
+ */
+ private int size;
+
+ /**
+ * The compilation context.
+ */
+ private JspCompilationContext context;
+
+ /**
+ * The Jasper error dispatcher.
+ */
+ private ErrorDispatcher err;
+
+ /**
+ * Set to true when using the JspReader on a single file where we read up
+ * to the end and reset to the beginning many times.
+ * (as in ParserController.figureOutJspDocument()).
+ */
+ private boolean singleFile;
+
+ /**
+ * Constructor.
+ *
+ * @param ctxt The compilation context
+ * @param fname The file name
+ * @param encoding The file encoding
+ * @param jarFile ?
+ * @param err The error dispatcher
+ * @throws JasperException If a Jasper-internal error occurs
+ * @throws FileNotFoundException If the JSP file is not found (or is unreadable)
+ * @throws IOException If an IO-level error occurs, e.g. reading the file
+ */
+ public JspReader(JspCompilationContext ctxt,
+ String fname,
+ String encoding,
+ JarFile jarFile,
+ ErrorDispatcher err)
+ throws JasperException, FileNotFoundException, IOException {
+
+ this(ctxt, fname, encoding,
+ JspUtil.getReader(fname, encoding, jarFile, ctxt, err),
+ err);
+ }
+
+ /**
+ * Constructor: same as above constructor but with initialized reader
+ * to the file given.
+ */
+ public JspReader(JspCompilationContext ctxt,
+ String fname,
+ String encoding,
+ InputStreamReader reader,
+ ErrorDispatcher err)
+ throws JasperException, FileNotFoundException {
+
+ this.context = ctxt;
+ this.err = err;
+ sourceFiles = new Vector();
+ currFileId = 0;
+ size = 0;
+ singleFile = false;
+ pushFile(fname, encoding, reader);
+ }
+
+ /**
+ * @return JSP compilation context with which this JspReader is
+ * associated
+ */
+ JspCompilationContext getJspCompilationContext() {
+ return context;
+ }
+
+ /**
+ * Returns the file at the given position in the list.
+ *
+ * @param fileid The file position in the list
+ * @return The file at that position, if found, null otherwise
+ */
+ String getFile(final int fileid) {
+ return (String) sourceFiles.get(fileid);
+ }
+
+ /**
+ * Checks if the current file has more input.
+ *
+ * @return True if more reading is possible
+ * @throws JasperException if an error occurs
+ */
+ boolean hasMoreInput() throws JasperException {
+ if (current.cursor >= current.stream.length) {
+ if (singleFile) return false;
+ while (popFile()) {
+ if (current.cursor < current.stream.length) return true;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ int nextChar() throws JasperException {
+ if (!hasMoreInput())
+ return -1;
+
+ int ch = current.stream[current.cursor];
+
+ current.cursor++;
+
+ if (ch == '\n') {
+ current.line++;
+ current.col = 0;
+ } else {
+ current.col++;
+ }
+ return ch;
+ }
+
+ /**
+ * Back up the current cursor by one char, assumes current.cursor > 0,
+ * and that the char to be pushed back is not '\n'.
+ */
+ void pushChar() {
+ current.cursor--;
+ current.col--;
+ }
+
+ String getText(Mark start, Mark stop) throws JasperException {
+ Mark oldstart = mark();
+ reset(start);
+ CharArrayWriter caw = new CharArrayWriter();
+ while (!stop.equals(mark()))
+ caw.write(nextChar());
+ caw.close();
+ reset(oldstart);
+ return caw.toString();
+ }
+
+ int peekChar() throws JasperException {
+ if (!hasMoreInput())
+ return -1;
+ return current.stream[current.cursor];
+ }
+
+ Mark mark() {
+ return new Mark(current);
+ }
+
+ void reset(Mark mark) {
+ current = new Mark(mark);
+ }
+
+ boolean matchesIgnoreCase(String string) throws JasperException {
+ Mark mark = mark();
+ int ch = 0;
+ int i = 0;
+ do {
+ ch = nextChar();
+ if (Character.toLowerCase((char) ch) != string.charAt(i++)) {
+ reset(mark);
+ return false;
+ }
+ } while (i < string.length());
+ reset(mark);
+ return true;
+ }
+
+ /**
+ * search the stream for a match to a string
+ * @param string The string to match
+ * @return <strong>true</strong> is one is found, the current position
+ * in stream is positioned after the search string, <strong>
+ * false</strong> otherwise, position in stream unchanged.
+ */
+ boolean matches(String string) throws JasperException {
+ Mark mark = mark();
+ int ch = 0;
+ int i = 0;
+ do {
+ ch = nextChar();
+ if (((char) ch) != string.charAt(i++)) {
+ reset(mark);
+ return false;
+ }
+ } while (i < string.length());
+ return true;
+ }
+
+ boolean matchesETag(String tagName) throws JasperException {
+ Mark mark = mark();
+
+ if (!matches("</" + tagName))
+ return false;
+ skipSpaces();
+ if (nextChar() == '>')
+ return true;
+
+ reset(mark);
+ return false;
+ }
+
+ boolean matchesETagWithoutLessThan(String tagName)
+ throws JasperException
+ {
+ Mark mark = mark();
+
+ if (!matches("/" + tagName))
+ return false;
+ skipSpaces();
+ if (nextChar() == '>')
+ return true;
+
+ reset(mark);
+ return false;
+ }
+
+
+ /**
+ * Looks ahead to see if there are optional spaces followed by
+ * the given String. If so, true is returned and those spaces and
+ * characters are skipped. If not, false is returned and the
+ * position is restored to where we were before.
+ */
+ boolean matchesOptionalSpacesFollowedBy( String s )
+ throws JasperException
+ {
+ Mark mark = mark();
+
+ skipSpaces();
+ boolean result = matches( s );
+ if( !result ) {
+ reset( mark );
+ }
+
+ return result;
+ }
+
+ int skipSpaces() throws JasperException {
+ int i = 0;
+ while (hasMoreInput() && isSpace()) {
+ i++;
+ nextChar();
+ }
+ return i;
+ }
+
+ /**
+ * Skip until the given string is matched in the stream.
+ * When returned, the context is positioned past the end of the match.
+ *
+ * @param s The String to match.
+ * @return A non-null <code>Mark</code> instance (positioned immediately
+ * before the search string) if found, <strong>null</strong>
+ * otherwise.
+ */
+ Mark skipUntil(String limit) throws JasperException {
+ Mark ret = null;
+ int limlen = limit.length();
+ int ch;
+
+ skip:
+ for (ret = mark(), ch = nextChar() ; ch != -1 ;
+ ret = mark(), ch = nextChar()) {
+ if (ch == limit.charAt(0)) {
+ Mark restart = mark();
+ for (int i = 1 ; i < limlen ; i++) {
+ if (peekChar() == limit.charAt(i))
+ nextChar();
+ else {
+ reset(restart);
+ continue skip;
+ }
+ }
+ return ret;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Skip until the given string is matched in the stream, but ignoring
+ * chars initially escaped by a '\'.
+ * When returned, the context is positioned past the end of the match.
+ *
+ * @param s The String to match.
+ * @return A non-null <code>Mark</code> instance (positioned immediately
+ * before the search string) if found, <strong>null</strong>
+ * otherwise.
+ */
+ Mark skipUntilIgnoreEsc(String limit) throws JasperException {
+ Mark ret = null;
+ int limlen = limit.length();
+ int ch;
+ int prev = 'x'; // Doesn't matter
+
+ skip:
+ for (ret = mark(), ch = nextChar() ; ch != -1 ;
+ ret = mark(), prev = ch, ch = nextChar()) {
+ if (ch == '\\' && prev == '\\') {
+ ch = 0; // Double \ is not an escape char anymore
+ }
+ else if (ch == limit.charAt(0) && prev != '\\') {
+ for (int i = 1 ; i < limlen ; i++) {
+ if (peekChar() == limit.charAt(i))
+ nextChar();
+ else
+ continue skip;
+ }
+ return ret;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Skip until the given end tag is matched in the stream.
+ * When returned, the context is positioned past the end of the tag.
+ *
+ * @param tag The name of the tag whose ETag (</tag>) to match.
+ * @return A non-null <code>Mark</code> instance (positioned immediately
+ * before the ETag) if found, <strong>null</strong> otherwise.
+ */
+ Mark skipUntilETag(String tag) throws JasperException {
+ Mark ret = skipUntil("</" + tag);
+ if (ret != null) {
+ skipSpaces();
+ if (nextChar() != '>')
+ ret = null;
+ }
+ return ret;
+ }
+
+ final boolean isSpace() throws JasperException {
+ // Note: If this logic changes, also update Node.TemplateText.rtrim()
+ return peekChar() <= ' ';
+ }
+
+ /**
+ * Parse a space delimited token.
+ * If quoted the token will consume all characters up to a matching quote,
+ * otherwise, it consumes up to the first delimiter character.
+ *
+ * @param quoted If <strong>true</strong> accept quoted strings.
+ */
+ String parseToken(boolean quoted) throws JasperException {
+ StringBuffer stringBuffer = new StringBuffer();
+ skipSpaces();
+ stringBuffer.setLength(0);
+
+ if (!hasMoreInput()) {
+ return "";
+ }
+
+ int ch = peekChar();
+
+ if (quoted) {
+ if (ch == '"' || ch == '\'') {
+
+ char endQuote = ch == '"' ? '"' : '\'';
+ // Consume the open quote:
+ ch = nextChar();
+ for (ch = nextChar(); ch != -1 && ch != endQuote;
+ ch = nextChar()) {
+ if (ch == '\\')
+ ch = nextChar();
+ stringBuffer.append((char) ch);
+ }
+ // Check end of quote, skip closing quote:
+ if (ch == -1) {
+ err.jspError(mark(), "jsp.error.quotes.unterminated");
+ }
+ } else {
+ err.jspError(mark(), "jsp.error.attr.quoted");
+ }
+ } else {
+ if (!isDelimiter()) {
+ // Read value until delimiter is found:
+ do {
+ ch = nextChar();
+ // Take care of the quoting here.
+ if (ch == '\\') {
+ if (peekChar() == '"' || peekChar() == '\'' ||
+ peekChar() == '>' || peekChar() == '%')
+ ch = nextChar();
+ }
+ stringBuffer.append((char) ch);
+ } while (!isDelimiter());
+ }
+ }
+
+ return stringBuffer.toString();
+ }
+
+ void setSingleFile(boolean val) {
+ singleFile = val;
+ }
+
+
+ /**
+ * Gets the URL for the given path name.
+ *
+ * @param path Path name
+ *
+ * @return URL for the given path name.
+ *
+ * @exception MalformedURLException if the path name is not given in
+ * the correct form
+ */
+ URL getResource(String path) throws MalformedURLException {
+ return context.getResource(path);
+ }
+
+
+ /**
+ * Parse utils - Is current character a token delimiter ?
+ * Delimiters are currently defined to be =, >, <, ", and ' or any
+ * any space character as defined by <code>isSpace</code>.
+ *
+ * @return A boolean.
+ */
+ private boolean isDelimiter() throws JasperException {
+ if (! isSpace()) {
+ int ch = peekChar();
+ // Look for a single-char work delimiter:
+ if (ch == '=' || ch == '>' || ch == '"' || ch == '\''
+ || ch == '/') {
+ return true;
+ }
+ // Look for an end-of-comment or end-of-tag:
+ if (ch == '-') {
+ Mark mark = mark();
+ if (((ch = nextChar()) == '>')
+ || ((ch == '-') && (nextChar() == '>'))) {
+ reset(mark);
+ return true;
+ } else {
+ reset(mark);
+ return false;
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Register a new source file.
+ * This method is used to implement file inclusion. Each included file
+ * gets a unique identifier (which is the index in the array of source
+ * files).
+ *
+ * @return The index of the now registered file.
+ */
+ private int registerSourceFile(final String file) {
+ if (sourceFiles.contains(file)) {
+ return -1;
+ }
+
+ sourceFiles.add(file);
+ this.size++;
+
+ return sourceFiles.size() - 1;
+ }
+
+
+ /**
+ * Unregister the source file.
+ * This method is used to implement file inclusion. Each included file
+ * gets a uniq identifier (which is the index in the array of source
+ * files).
+ *
+ * @return The index of the now registered file.
+ */
+ private int unregisterSourceFile(final String file) {
+ if (!sourceFiles.contains(file)) {
+ return -1;
+ }
+
+ sourceFiles.remove(file);
+ this.size--;
+ return sourceFiles.size() - 1;
+ }
+
+ /**
+ * Push a file (and its associated Stream) on the file stack. THe
+ * current position in the current file is remembered.
+ */
+ private void pushFile(String file, String encoding,
+ InputStreamReader reader)
+ throws JasperException, FileNotFoundException {
+
+ // Register the file
+ String longName = file;
+
+ int fileid = registerSourceFile(longName);
+
+ if (fileid == -1) {
+ // Bugzilla 37407: http://issues.apache.org/bugzilla/show_bug.cgi?id=37407
+ if(reader != null) {
+ try {
+ reader.close();
+ } catch (Exception any) {
+ if(log.isDebugEnabled()) {
+ log.debug("Exception closing reader: ", any);
+ }
+ }
+ }
+
+ err.jspError("jsp.error.file.already.registered", file);
+ }
+
+ currFileId = fileid;
+
+ try {
+ CharArrayWriter caw = new CharArrayWriter();
+ char buf[] = new char[1024];
+ for (int i = 0 ; (i = reader.read(buf)) != -1 ;)
+ caw.write(buf, 0, i);
+ caw.close();
+ if (current == null) {
+ current = new Mark(this, caw.toCharArray(), fileid,
+ getFile(fileid), master, encoding);
+ } else {
+ current.pushStream(caw.toCharArray(), fileid, getFile(fileid),
+ longName, encoding);
+ }
+ } catch (Throwable ex) {
+ log.error("Exception parsing file ", ex);
+ // Pop state being constructed:
+ popFile();
+ err.jspError("jsp.error.file.cannot.read", file);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (Exception any) {
+ if(log.isDebugEnabled()) {
+ log.debug("Exception closing reader: ", any);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Pop a file from the file stack. The field "current" is retored
+ * to the value to point to the previous files, if any, and is set
+ * to null otherwise.
+ * @return true is there is a previous file on the stack.
+ * false otherwise.
+ */
+ private boolean popFile() throws JasperException {
+
+ // Is stack created ? (will happen if the Jsp file we're looking at is
+ // missing.
+ if (current == null || currFileId < 0) {
+ return false;
+ }
+
+ // Restore parser state:
+ String fName = getFile(currFileId);
+ currFileId = unregisterSourceFile(fName);
+ if (currFileId < -1) {
+ err.jspError("jsp.error.file.not.registered", fName);
+ }
+
+ Mark previous = current.popStream();
+ if (previous != null) {
+ master = current.baseDir;
+ current = previous;
+ return true;
+ }
+ // Note that although the current file is undefined here, "current"
+ // is not set to null just for convience, for it maybe used to
+ // set the current (undefined) position.
+ return false;
+ }
+}
+
Added: struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspRuntimeContext.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspRuntimeContext.java?rev=799681&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspRuntimeContext.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspRuntimeContext.java Fri Jul 31 18:12:48 2009
@@ -0,0 +1,525 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.struts2.jasper.compiler;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FilePermission;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.CodeSource;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.jsp.JspFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.jasper.Constants;
+import org.apache.struts2.jasper.JspCompilationContext;
+import org.apache.struts2.jasper.Options;
+import org.apache.struts2.jasper.runtime.JspFactoryImpl;
+import org.apache.struts2.jasper.security.SecurityClassLoad;
+import org.apache.struts2.jasper.servlet.JspServletWrapper;
+
+/**
+ * Class for tracking JSP compile time file dependencies when the
+ * &060;%@include file="..."%&062; directive is used.
+ *
+ * A background thread periodically checks the files a JSP page
+ * is dependent upon. If a dpendent file changes the JSP page
+ * which included it is recompiled.
+ *
+ * Only used if a web application context is a directory.
+ *
+ * @author Glenn L. Nielsen
+ * @version $Revision: 466606 $
+ */
+public final class JspRuntimeContext implements Runnable {
+
+ // Logger
+ private Log log = LogFactory.getLog(JspRuntimeContext.class);
+
+ /*
+ * Counts how many times the webapp's JSPs have been reloaded.
+ */
+ private int jspReloadCount;
+
+ /**
+ * Preload classes required at runtime by a JSP servlet so that
+ * we don't get a defineClassInPackage security exception.
+ */
+ static {
+ JspFactoryImpl factory = new JspFactoryImpl();
+ SecurityClassLoad.securityClassLoad(factory.getClass().getClassLoader());
+ JspFactory.setDefaultFactory(factory);
+ }
+
+ // ----------------------------------------------------------- Constructors
+
+ /**
+ * Create a JspRuntimeContext for a web application context.
+ *
+ * Loads in any previously generated dependencies from file.
+ *
+ * @param context ServletContext for web application
+ */
+ public JspRuntimeContext(ServletContext context, Options options) {
+
+ this.context = context;
+ this.options = options;
+
+ // Get the parent class loader
+ parentClassLoader =
+ (URLClassLoader) Thread.currentThread().getContextClassLoader();
+ if (parentClassLoader == null) {
+ parentClassLoader =
+ (URLClassLoader)this.getClass().getClassLoader();
+ }
+
+ if (log.isDebugEnabled()) {
+ if (parentClassLoader != null) {
+ log.debug(Localizer.getMessage("jsp.message.parent_class_loader_is",
+ parentClassLoader.toString()));
+ } else {
+ log.debug(Localizer.getMessage("jsp.message.parent_class_loader_is",
+ "<none>"));
+ }
+ }
+
+ initClassPath();
+
+ if (context instanceof org.apache.struts2.jasper.servlet.JspCServletContext) {
+ return;
+ }
+
+ if (System.getSecurityManager() != null) {
+ initSecurity();
+ }
+
+ // If this web application context is running from a
+ // directory, start the background compilation thread
+ String appBase = context.getRealPath("/");
+ if (!options.getDevelopment()
+ && appBase != null
+ && options.getCheckInterval() > 0) {
+ if (appBase.endsWith(File.separator) ) {
+ appBase = appBase.substring(0,appBase.length()-1);
+ }
+ String directory =
+ appBase.substring(appBase.lastIndexOf(File.separator));
+ threadName = threadName + "[" + directory + "]";
+ threadStart();
+ }
+ }
+
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * This web applications ServletContext
+ */
+ private ServletContext context;
+ private Options options;
+ private URLClassLoader parentClassLoader;
+ private PermissionCollection permissionCollection;
+ private CodeSource codeSource;
+ private String classpath;
+
+ /**
+ * Maps JSP pages to their JspServletWrapper's
+ */
+ private Map jsps = Collections.synchronizedMap( new HashMap());
+
+
+ /**
+ * The background thread.
+ */
+ private Thread thread = null;
+
+
+ /**
+ * The background thread completion semaphore.
+ */
+ private boolean threadDone = false;
+
+
+ /**
+ * Name to register for the background thread.
+ */
+ private String threadName = "JspRuntimeContext";
+
+ // ------------------------------------------------------ Public Methods
+
+ /**
+ * Add a new JspServletWrapper.
+ *
+ * @param jspUri JSP URI
+ * @param jsw Servlet wrapper for JSP
+ */
+ public void addWrapper(String jspUri, JspServletWrapper jsw) {
+ jsps.remove(jspUri);
+ jsps.put(jspUri,jsw);
+ }
+
+ /**
+ * Get an already existing JspServletWrapper.
+ *
+ * @param jspUri JSP URI
+ * @return JspServletWrapper for JSP
+ */
+ public JspServletWrapper getWrapper(String jspUri) {
+ return (JspServletWrapper) jsps.get(jspUri);
+ }
+
+ /**
+ * Remove a JspServletWrapper.
+ *
+ * @param jspUri JSP URI of JspServletWrapper to remove
+ */
+ public void removeWrapper(String jspUri) {
+ jsps.remove(jspUri);
+ }
+
+ /**
+ * Returns the number of JSPs for which JspServletWrappers exist, i.e.,
+ * the number of JSPs that have been loaded into the webapp.
+ *
+ * @return The number of JSPs that have been loaded into the webapp
+ */
+ public int getJspCount() {
+ return jsps.size();
+ }
+
+ /**
+ * Get the SecurityManager Policy CodeSource for this web
+ * applicaiton context.
+ *
+ * @return CodeSource for JSP
+ */
+ public CodeSource getCodeSource() {
+ return codeSource;
+ }
+
+ /**
+ * Get the parent URLClassLoader.
+ *
+ * @return URLClassLoader parent
+ */
+ public URLClassLoader getParentClassLoader() {
+ return parentClassLoader;
+ }
+
+ /**
+ * Get the SecurityManager PermissionCollection for this
+ * web application context.
+ *
+ * @return PermissionCollection permissions
+ */
+ public PermissionCollection getPermissionCollection() {
+ return permissionCollection;
+ }
+
+ /**
+ * Process a "destory" event for this web application context.
+ */
+ public void destroy() {
+ threadStop();
+
+ Iterator servlets = jsps.values().iterator();
+ while (servlets.hasNext()) {
+ ((JspServletWrapper) servlets.next()).destroy();
+ }
+ }
+
+ /**
+ * Increments the JSP reload counter.
+ */
+ public synchronized void incrementJspReloadCount() {
+ jspReloadCount++;
+ }
+
+ /**
+ * Resets the JSP reload counter.
+ *
+ * @param count Value to which to reset the JSP reload counter
+ */
+ public synchronized void setJspReloadCount(int count) {
+ this.jspReloadCount = count;
+ }
+
+ /**
+ * Gets the current value of the JSP reload counter.
+ *
+ * @return The current value of the JSP reload counter
+ */
+ public int getJspReloadCount() {
+ return jspReloadCount;
+ }
+
+
+ // -------------------------------------------------------- Private Methods
+
+ /**
+ * Method used by background thread to check the JSP dependencies
+ * registered with this class for JSP's.
+ */
+ private void checkCompile() {
+ Object [] wrappers = jsps.values().toArray();
+ for (int i = 0; i < wrappers.length; i++ ) {
+ JspServletWrapper jsw = (JspServletWrapper)wrappers[i];
+ JspCompilationContext ctxt = jsw.getJspEngineContext();
+ // JspServletWrapper also synchronizes on this when
+ // it detects it has to do a reload
+ synchronized(jsw) {
+ try {
+ ctxt.compile();
+ } catch (FileNotFoundException ex) {
+ ctxt.incrementRemoved();
+ } catch (Throwable t) {
+ jsw.getServletContext().log("Background compile failed",
+ t);
+ }
+ }
+ }
+ }
+
+ /**
+ * The classpath that is passed off to the Java compiler.
+ */
+ public String getClassPath() {
+ return classpath;
+ }
+
+ /**
+ * Method used to initialize classpath for compiles.
+ */
+ private void initClassPath() {
+
+ URL [] urls = parentClassLoader.getURLs();
+ StringBuffer cpath = new StringBuffer();
+ String sep = System.getProperty("path.separator");
+
+ for(int i = 0; i < urls.length; i++) {
+ // Tomcat 4 can use URL's other than file URL's,
+ // a protocol other than file: will generate a
+ // bad file system path, so only add file:
+ // protocol URL's to the classpath.
+
+ if( urls[i].getProtocol().equals("file") ) {
+ cpath.append((String)urls[i].getFile()+sep);
+ }
+ }
+
+ cpath.append(options.getScratchDir() + sep);
+
+ String cp = (String) context.getAttribute(Constants.SERVLET_CLASSPATH);
+ if (cp == null || cp.equals("")) {
+ cp = options.getClassPath();
+ }
+
+ classpath = cpath.toString() + cp;
+
+ if(log.isDebugEnabled()) {
+ log.debug("Compilation classpath initialized: " + getClassPath());
+ }
+ }
+
+ /**
+ * Method used to initialize SecurityManager data.
+ */
+ private void initSecurity() {
+
+ // Setup the PermissionCollection for this web app context
+ // based on the permissions configured for the root of the
+ // web app context directory, then add a file read permission
+ // for that directory.
+ Policy policy = Policy.getPolicy();
+ if( policy != null ) {
+ try {
+ // Get the permissions for the web app context
+ String docBase = context.getRealPath("/");
+ if( docBase == null ) {
+ docBase = options.getScratchDir().toString();
+ }
+ String codeBase = docBase;
+ if (!codeBase.endsWith(File.separator)){
+ codeBase = codeBase + File.separator;
+ }
+ File contextDir = new File(codeBase);
+ URL url = contextDir.getCanonicalFile().toURL();
+ codeSource = new CodeSource(url,(Certificate[])null);
+ permissionCollection = policy.getPermissions(codeSource);
+
+ // Create a file read permission for web app context directory
+ if (!docBase.endsWith(File.separator)){
+ permissionCollection.add
+ (new FilePermission(docBase,"read"));
+ docBase = docBase + File.separator;
+ } else {
+ permissionCollection.add
+ (new FilePermission
+ (docBase.substring(0,docBase.length() - 1),"read"));
+ }
+ docBase = docBase + "-";
+ permissionCollection.add(new FilePermission(docBase,"read"));
+
+ // Create a file read permission for web app tempdir (work)
+ // directory
+ String workDir = options.getScratchDir().toString();
+ if (!workDir.endsWith(File.separator)){
+ permissionCollection.add
+ (new FilePermission(workDir,"read"));
+ workDir = workDir + File.separator;
+ }
+ workDir = workDir + "-";
+ permissionCollection.add(new FilePermission(workDir,"read"));
+
+ // Allow the JSP to access org.apache.struts2.jasper.runtime.HttpJspBase
+ permissionCollection.add( new RuntimePermission(
+ "accessClassInPackage.org.apache.struts2.jasper.runtime") );
+
+ if (parentClassLoader instanceof URLClassLoader) {
+ URL [] urls = parentClassLoader.getURLs();
+ String jarUrl = null;
+ String jndiUrl = null;
+ for (int i=0; i<urls.length; i++) {
+ if (jndiUrl == null
+ && urls[i].toString().startsWith("jndi:") ) {
+ jndiUrl = urls[i].toString() + "-";
+ }
+ if (jarUrl == null
+ && urls[i].toString().startsWith("jar:jndi:")
+ ) {
+ jarUrl = urls[i].toString();
+ jarUrl = jarUrl.substring(0,jarUrl.length() - 2);
+ jarUrl = jarUrl.substring(0,
+ jarUrl.lastIndexOf('/')) + "/-";
+ }
+ }
+ if (jarUrl != null) {
+ permissionCollection.add(
+ new FilePermission(jarUrl,"read"));
+ permissionCollection.add(
+ new FilePermission(jarUrl.substring(4),"read"));
+ }
+ if (jndiUrl != null)
+ permissionCollection.add(
+ new FilePermission(jndiUrl,"read") );
+ }
+ } catch(Exception e) {
+ context.log("Security Init for context failed",e);
+ }
+ }
+ }
+
+
+ // -------------------------------------------------------- Thread Support
+
+ /**
+ * Start the background thread that will periodically check for
+ * changes to compile time included files in a JSP.
+ *
+ * @exception IllegalStateException if we should not be starting
+ * a background thread now
+ */
+ protected void threadStart() {
+
+ // Has the background thread already been started?
+ if (thread != null) {
+ return;
+ }
+
+ // Start the background thread
+ threadDone = false;
+ thread = new Thread(this, threadName);
+ thread.setDaemon(true);
+ thread.start();
+
+ }
+
+
+ /**
+ * Stop the background thread that is periodically checking for
+ * changes to compile time included files in a JSP.
+ */
+ protected void threadStop() {
+
+ if (thread == null) {
+ return;
+ }
+
+ threadDone = true;
+ thread.interrupt();
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ ;
+ }
+
+ thread = null;
+
+ }
+
+ /**
+ * Sleep for the duration specified by the <code>checkInterval</code>
+ * property.
+ */
+ protected void threadSleep() {
+
+ try {
+ Thread.sleep(options.getCheckInterval() * 1000L);
+ } catch (InterruptedException e) {
+ ;
+ }
+
+ }
+
+
+ // ------------------------------------------------------ Background Thread
+
+
+ /**
+ * The background thread that checks for changes to files
+ * included by a JSP and flags that a recompile is required.
+ */
+ public void run() {
+
+ // Loop until the termination semaphore is set
+ while (!threadDone) {
+
+ // Wait for our check interval
+ threadSleep();
+
+ // Check for included files which are newer than the
+ // JSP which uses them.
+ try {
+ checkCompile();
+ } catch (Throwable t) {
+ log.error("Exception checking if recompile needed: ", t);
+ }
+ }
+
+ }
+
+}
Added: struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspUtil.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspUtil.java?rev=799681&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspUtil.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/JspUtil.java Fri Jul 31 18:12:48 2009
@@ -0,0 +1,1109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.struts2.jasper.compiler;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.Vector;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import javax.servlet.jsp.el.ELException;
+import javax.servlet.jsp.el.ELParseException;
+import javax.servlet.jsp.el.FunctionMapper;
+
+import org.apache.commons.el.ExpressionEvaluatorImpl;
+import org.apache.struts2.jasper.Constants;
+import org.apache.struts2.jasper.JasperException;
+import org.apache.struts2.jasper.JspCompilationContext;
+import org.xml.sax.Attributes;
+
+/**
+ * This class has all the utility method(s).
+ * Ideally should move all the bean containers here.
+ *
+ * @author Mandar Raje.
+ * @author Rajiv Mordani.
+ * @author Danno Ferrin
+ * @author Pierre Delisle
+ * @author Shawn Bayern
+ * @author Mark Roth
+ */
+public class JspUtil {
+
+ private static final String WEB_INF_TAGS = "/WEB-INF/tags/";
+ private static final String META_INF_TAGS = "/META-INF/tags/";
+
+ // Delimiters for request-time expressions (JSP and XML syntax)
+ private static final String OPEN_EXPR = "<%=";
+ private static final String CLOSE_EXPR = "%>";
+ private static final String OPEN_EXPR_XML = "%=";
+ private static final String CLOSE_EXPR_XML = "%";
+
+ private static int tempSequenceNumber = 0;
+ private static ExpressionEvaluatorImpl expressionEvaluator
+ = new ExpressionEvaluatorImpl();
+
+ private static final String javaKeywords[] = {
+ "abstract", "assert", "boolean", "break", "byte", "case",
+ "catch", "char", "class", "const", "continue",
+ "default", "do", "double", "else", "enum", "extends",
+ "final", "finally", "float", "for", "goto",
+ "if", "implements", "import", "instanceof", "int",
+ "interface", "long", "native", "new", "package",
+ "private", "protected", "public", "return", "short",
+ "static", "strictfp", "super", "switch", "synchronized",
+ "this", "throws", "transient", "try", "void",
+ "volatile", "while" };
+
+ public static final int CHUNKSIZE = 1024;
+
+ public static char[] removeQuotes(char []chars) {
+ CharArrayWriter caw = new CharArrayWriter();
+ for (int i = 0; i < chars.length; i++) {
+ if (chars[i] == '%' && chars[i+1] == '\\' &&
+ chars[i+2] == '>') {
+ caw.write('%');
+ caw.write('>');
+ i = i + 2;
+ } else {
+ caw.write(chars[i]);
+ }
+ }
+ return caw.toCharArray();
+ }
+
+ public static char[] escapeQuotes (char []chars) {
+ // Prescan to convert %\> to %>
+ String s = new String(chars);
+ while (true) {
+ int n = s.indexOf("%\\>");
+ if (n < 0)
+ break;
+ StringBuffer sb = new StringBuffer(s.substring(0, n));
+ sb.append("%>");
+ sb.append(s.substring(n + 3));
+ s = sb.toString();
+ }
+ chars = s.toCharArray();
+ return (chars);
+
+
+ // Escape all backslashes not inside a Java string literal
+ /*
+ CharArrayWriter caw = new CharArrayWriter();
+ boolean inJavaString = false;
+ for (int i = 0; i < chars.length; i++) {
+ if (chars[i] == '"') inJavaString = !inJavaString;
+ // escape out the escape character
+ if (!inJavaString && (chars[i] == '\\')) caw.write('\\');
+ caw.write(chars[i]);
+ }
+ return caw.toCharArray();
+ */
+ }
+
+ /**
+ * Checks if the token is a runtime expression.
+ * In standard JSP syntax, a runtime expression starts with '<%' and
+ * ends with '%>'. When the JSP document is in XML syntax, a runtime
+ * expression starts with '%=' and ends with '%'.
+ *
+ * @param token The token to be checked
+ * return whether the token is a runtime expression or not.
+ */
+ public static boolean isExpression(String token, boolean isXml) {
+ String openExpr;
+ String closeExpr;
+ if (isXml) {
+ openExpr = OPEN_EXPR_XML;
+ closeExpr = CLOSE_EXPR_XML;
+ } else {
+ openExpr = OPEN_EXPR;
+ closeExpr = CLOSE_EXPR;
+ }
+ if (token.startsWith(openExpr) && token.endsWith(closeExpr)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @return the "expression" part of a runtime expression,
+ * taking the delimiters out.
+ */
+ public static String getExpr (String expression, boolean isXml) {
+ String returnString;
+ String openExpr;
+ String closeExpr;
+ if (isXml) {
+ openExpr = OPEN_EXPR_XML;
+ closeExpr = CLOSE_EXPR_XML;
+ } else {
+ openExpr = OPEN_EXPR;
+ closeExpr = CLOSE_EXPR;
+ }
+ int length = expression.length();
+ if (expression.startsWith(openExpr) &&
+ expression.endsWith(closeExpr)) {
+ returnString = expression.substring(
+ openExpr.length(), length - closeExpr.length());
+ } else {
+ returnString = "";
+ }
+ return returnString;
+ }
+
+ /**
+ * Takes a potential expression and converts it into XML form
+ */
+ public static String getExprInXml(String expression) {
+ String returnString;
+ int length = expression.length();
+
+ if (expression.startsWith(OPEN_EXPR)
+ && expression.endsWith(CLOSE_EXPR)) {
+ returnString = expression.substring (1, length - 1);
+ } else {
+ returnString = expression;
+ }
+
+ return escapeXml(returnString.replace(Constants.HACK_CHAR, '$'));
+ }
+
+ /**
+ * Checks to see if the given scope is valid.
+ *
+ * @param scope The scope to be checked
+ * @param n The Node containing the 'scope' attribute whose value is to be
+ * checked
+ * @param err error dispatcher
+ *
+ * @throws JasperException if scope is not null and different from
+ * "page", "request", "session", and
+ * "application"
+ */
+ public static void checkScope(String scope, Node n, ErrorDispatcher err)
+ throws JasperException {
+ if (scope != null && !scope.equals("page") && !scope.equals("request")
+ && !scope.equals("session") && !scope.equals("application")) {
+ err.jspError(n, "jsp.error.invalid.scope", scope);
+ }
+ }
+
+ /**
+ * Checks if all mandatory attributes are present and if all attributes
+ * present have valid names. Checks attributes specified as XML-style
+ * attributes as well as attributes specified using the jsp:attribute
+ * standard action.
+ */
+ public static void checkAttributes(String typeOfTag,
+ Node n,
+ ValidAttribute[] validAttributes,
+ ErrorDispatcher err)
+ throws JasperException {
+ Attributes attrs = n.getAttributes();
+ Mark start = n.getStart();
+ boolean valid = true;
+
+ // AttributesImpl.removeAttribute is broken, so we do this...
+ int tempLength = (attrs == null) ? 0 : attrs.getLength();
+ Vector temp = new Vector(tempLength, 1);
+ for (int i = 0; i < tempLength; i++) {
+ String qName = attrs.getQName(i);
+ if ((!qName.equals("xmlns")) && (!qName.startsWith("xmlns:")))
+ temp.addElement(qName);
+ }
+
+ // Add names of attributes specified using jsp:attribute
+ Node.Nodes tagBody = n.getBody();
+ if( tagBody != null ) {
+ int numSubElements = tagBody.size();
+ for( int i = 0; i < numSubElements; i++ ) {
+ Node node = tagBody.getNode( i );
+ if( node instanceof Node.NamedAttribute ) {
+ String attrName = node.getAttributeValue( "name" );
+ temp.addElement( attrName );
+ // Check if this value appear in the attribute of the node
+ if (n.getAttributeValue(attrName) != null) {
+ err.jspError(n, "jsp.error.duplicate.name.jspattribute",
+ attrName);
+ }
+ }
+ else {
+ // Nothing can come before jsp:attribute, and only
+ // jsp:body can come after it.
+ break;
+ }
+ }
+ }
+
+ /*
+ * First check to see if all the mandatory attributes are present.
+ * If so only then proceed to see if the other attributes are valid
+ * for the particular tag.
+ */
+ String missingAttribute = null;
+
+ for (int i = 0; i < validAttributes.length; i++) {
+ int attrPos;
+ if (validAttributes[i].mandatory) {
+ attrPos = temp.indexOf(validAttributes[i].name);
+ if (attrPos != -1) {
+ temp.remove(attrPos);
+ valid = true;
+ } else {
+ valid = false;
+ missingAttribute = validAttributes[i].name;
+ break;
+ }
+ }
+ }
+
+ // If mandatory attribute is missing then the exception is thrown
+ if (!valid)
+ err.jspError(start, "jsp.error.mandatory.attribute", typeOfTag,
+ missingAttribute);
+
+ // Check to see if there are any more attributes for the specified tag.
+ int attrLeftLength = temp.size();
+ if (attrLeftLength == 0)
+ return;
+
+ // Now check to see if the rest of the attributes are valid too.
+ String attribute = null;
+
+ for (int j = 0; j < attrLeftLength; j++) {
+ valid = false;
+ attribute = (String) temp.elementAt(j);
+ for (int i = 0; i < validAttributes.length; i++) {
+ if (attribute.equals(validAttributes[i].name)) {
+ valid = true;
+ break;
+ }
+ }
+ if (!valid)
+ err.jspError(start, "jsp.error.invalid.attribute", typeOfTag,
+ attribute);
+ }
+ // XXX *could* move EL-syntax validation here... (sb)
+ }
+
+ public static String escapeQueryString(String unescString) {
+ if ( unescString == null )
+ return null;
+
+ String escString = "";
+ String shellSpChars = "\\\"";
+
+ for(int index=0; index<unescString.length(); index++) {
+ char nextChar = unescString.charAt(index);
+
+ if( shellSpChars.indexOf(nextChar) != -1 )
+ escString += "\\";
+
+ escString += nextChar;
+ }
+ return escString;
+ }
+
+ /**
+ * Escape the 5 entities defined by XML.
+ */
+ public static String escapeXml(String s) {
+ if (s == null) return null;
+ StringBuffer sb = new StringBuffer();
+ for(int i=0; i<s.length(); i++) {
+ char c = s.charAt(i);
+ if (c == '<') {
+ sb.append("<");
+ } else if (c == '>') {
+ sb.append(">");
+ } else if (c == '\'') {
+ sb.append("'");
+ } else if (c == '&') {
+ sb.append("&");
+ } else if (c == '"') {
+ sb.append(""");
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Replaces any occurrences of the character <tt>replace</tt> with the
+ * string <tt>with</tt>.
+ */
+ public static String replace(String name, char replace, String with) {
+ StringBuffer buf = new StringBuffer();
+ int begin = 0;
+ int end;
+ int last = name.length();
+
+ while (true) {
+ end = name.indexOf(replace, begin);
+ if (end < 0) {
+ end = last;
+ }
+ buf.append(name.substring(begin, end));
+ if (end == last) {
+ break;
+ }
+ buf.append(with);
+ begin = end + 1;
+ }
+
+ return buf.toString();
+ }
+
+ public static class ValidAttribute {
+ String name;
+ boolean mandatory;
+ boolean rtexprvalue; // not used now
+
+ public ValidAttribute (String name, boolean mandatory,
+ boolean rtexprvalue )
+ {
+ this.name = name;
+ this.mandatory = mandatory;
+ this.rtexprvalue = rtexprvalue;
+ }
+
+ public ValidAttribute (String name, boolean mandatory) {
+ this( name, mandatory, false );
+ }
+
+ public ValidAttribute (String name) {
+ this (name, false);
+ }
+ }
+
+ /**
+ * Convert a String value to 'boolean'.
+ * Besides the standard conversions done by
+ * Boolean.valueOf(s).booleanValue(), the value "yes"
+ * (ignore case) is also converted to 'true'.
+ * If 's' is null, then 'false' is returned.
+ *
+ * @param s the string to be converted
+ * @return the boolean value associated with the string s
+ */
+ public static boolean booleanValue(String s) {
+ boolean b = false;
+ if (s != null) {
+ if (s.equalsIgnoreCase("yes")) {
+ b = true;
+ } else {
+ b = Boolean.valueOf(s).booleanValue();
+ }
+ }
+ return b;
+ }
+
+ /**
+ * Returns the <tt>Class</tt> object associated with the class or
+ * interface with the given string name.
+ *
+ * <p> The <tt>Class</tt> object is determined by passing the given string
+ * name to the <tt>Class.forName()</tt> method, unless the given string
+ * name represents a primitive type, in which case it is converted to a
+ * <tt>Class</tt> object by appending ".class" to it (e.g., "int.class").
+ */
+ public static Class toClass(String type, ClassLoader loader)
+ throws ClassNotFoundException {
+
+ Class c = null;
+ int i0 = type.indexOf('[');
+ int dims = 0;
+ if (i0 > 0) {
+ // This is an array. Count the dimensions
+ for (int i = 0; i < type.length(); i++) {
+ if (type.charAt(i) == '[')
+ dims++;
+ }
+ type = type.substring(0, i0);
+ }
+
+ if ("boolean".equals(type))
+ c = boolean.class;
+ else if ("char".equals(type))
+ c = char.class;
+ else if ("byte".equals(type))
+ c = byte.class;
+ else if ("short".equals(type))
+ c = short.class;
+ else if ("int".equals(type))
+ c = int.class;
+ else if ("long".equals(type))
+ c = long.class;
+ else if ("float".equals(type))
+ c = float.class;
+ else if ("double".equals(type))
+ c = double.class;
+ else if (type.indexOf('[') < 0)
+ c = loader.loadClass(type);
+
+ if (dims == 0)
+ return c;
+
+ if (dims == 1)
+ return java.lang.reflect.Array.newInstance(c, 1).getClass();
+
+ // Array of more than i dimension
+ return java.lang.reflect.Array.newInstance(c, new int[dims]).getClass();
+ }
+
+ /**
+ * Produces a String representing a call to the EL interpreter.
+ * @param expression a String containing zero or more "${}" expressions
+ * @param expectedType the expected type of the interpreted result
+ * @param fnmapvar Variable pointing to a function map.
+ * @param XmlEscape True if the result should do XML escaping
+ * @return a String representing a call to the EL interpreter.
+ */
+ public static String interpreterCall(boolean isTagFile,
+ String expression,
+ Class expectedType,
+ String fnmapvar,
+ boolean XmlEscape )
+ {
+ /*
+ * Determine which context object to use.
+ */
+ String jspCtxt = null;
+ if (isTagFile)
+ jspCtxt = "this.getJspContext()";
+ else
+ jspCtxt = "_jspx_page_context";
+
+ /*
+ * Determine whether to use the expected type's textual name
+ * or, if it's a primitive, the name of its correspondent boxed
+ * type.
+ */
+ String targetType = expectedType.getName();
+ String primitiveConverterMethod = null;
+ if (expectedType.isPrimitive()) {
+ if (expectedType.equals(Boolean.TYPE)) {
+ targetType = Boolean.class.getName();
+ primitiveConverterMethod = "booleanValue";
+ } else if (expectedType.equals(Byte.TYPE)) {
+ targetType = Byte.class.getName();
+ primitiveConverterMethod = "byteValue";
+ } else if (expectedType.equals(Character.TYPE)) {
+ targetType = Character.class.getName();
+ primitiveConverterMethod = "charValue";
+ } else if (expectedType.equals(Short.TYPE)) {
+ targetType = Short.class.getName();
+ primitiveConverterMethod = "shortValue";
+ } else if (expectedType.equals(Integer.TYPE)) {
+ targetType = Integer.class.getName();
+ primitiveConverterMethod = "intValue";
+ } else if (expectedType.equals(Long.TYPE)) {
+ targetType = Long.class.getName();
+ primitiveConverterMethod = "longValue";
+ } else if (expectedType.equals(Float.TYPE)) {
+ targetType = Float.class.getName();
+ primitiveConverterMethod = "floatValue";
+ } else if (expectedType.equals(Double.TYPE)) {
+ targetType = Double.class.getName();
+ primitiveConverterMethod = "doubleValue";
+ }
+ }
+
+ if (primitiveConverterMethod != null) {
+ XmlEscape = false;
+ }
+
+ /*
+ * Build up the base call to the interpreter.
+ */
+ // XXX - We use a proprietary call to the interpreter for now
+ // as the current standard machinery is inefficient and requires
+ // lots of wrappers and adapters. This should all clear up once
+ // the EL interpreter moves out of JSTL and into its own project.
+ // In the future, this should be replaced by code that calls
+ // ExpressionEvaluator.parseExpression() and then cache the resulting
+ // expression objects. The interpreterCall would simply select
+ // one of the pre-cached expressions and evaluate it.
+ // Note that PageContextImpl implements VariableResolver and
+ // the generated Servlet/SimpleTag implements FunctionMapper, so
+ // that machinery is already in place (mroth).
+ targetType = toJavaSourceType(targetType);
+ StringBuffer call = new StringBuffer(
+ "(" + targetType + ") "
+ + "org.apache.struts2.jasper.runtime.PageContextImpl.proprietaryEvaluate"
+ + "(" + Generator.quote(expression) + ", "
+ + targetType + ".class, "
+ + "(PageContext)" + jspCtxt
+ + ", " + fnmapvar
+ + ", " + XmlEscape
+ + ")");
+
+ /*
+ * Add the primitive converter method if we need to.
+ */
+ if (primitiveConverterMethod != null) {
+ call.insert(0, "(");
+ call.append(")." + primitiveConverterMethod + "()");
+ }
+
+ return call.toString();
+ }
+
+ /**
+ * Validates the syntax of all ${} expressions within the given string.
+ * @param where the approximate location of the expressions in the JSP page
+ * @param expressions a string containing zero or more "${}" expressions
+ * @param err an error dispatcher to use
+ */
+ public static void validateExpressions(Mark where,
+ String expressions,
+ Class expectedType,
+ FunctionMapper functionMapper,
+ ErrorDispatcher err)
+ throws JasperException {
+
+ try {
+ JspUtil.expressionEvaluator.parseExpression( expressions,
+ expectedType, null );
+ }
+ catch( ELParseException e ) {
+ err.jspError(where, "jsp.error.invalid.expression", expressions,
+ e.toString() );
+ }
+ catch( ELException e ) {
+ err.jspError(where, "jsp.error.invalid.expression", expressions,
+ e.toString() );
+ }
+ }
+
+ /**
+ * Resets the temporary variable name.
+ * (not thread-safe)
+ */
+ public static void resetTemporaryVariableName() {
+ tempSequenceNumber = 0;
+ }
+
+ /**
+ * Generates a new temporary variable name.
+ * (not thread-safe)
+ */
+ public static String nextTemporaryVariableName() {
+ return Constants.TEMP_VARIABLE_NAME_PREFIX + (tempSequenceNumber++);
+ }
+
+ public static String coerceToPrimitiveBoolean(String s,
+ boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToBoolean(" + s + ")";
+ } else {
+ if (s == null || s.length() == 0)
+ return "false";
+ else
+ return Boolean.valueOf(s).toString();
+ }
+ }
+
+ public static String coerceToBoolean(String s, boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "(Boolean) org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Boolean.class)";
+ } else {
+ if (s == null || s.length() == 0) {
+ return "new Boolean(false)";
+ } else {
+ // Detect format error at translation time
+ return "new Boolean(" + Boolean.valueOf(s).toString() + ")";
+ }
+ }
+ }
+
+ public static String coerceToPrimitiveByte(String s,
+ boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToByte(" + s + ")";
+ } else {
+ if (s == null || s.length() == 0)
+ return "(byte) 0";
+ else
+ return "((byte)" + Byte.valueOf(s).toString() + ")";
+ }
+ }
+
+ public static String coerceToByte(String s, boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "(Byte) org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Byte.class)";
+ } else {
+ if (s == null || s.length() == 0) {
+ return "new Byte((byte) 0)";
+ } else {
+ // Detect format error at translation time
+ return "new Byte((byte)" + Byte.valueOf(s).toString() + ")";
+ }
+ }
+ }
+
+ public static String coerceToChar(String s, boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToChar(" + s + ")";
+ } else {
+ if (s == null || s.length() == 0) {
+ return "(char) 0";
+ } else {
+ char ch = s.charAt(0);
+ // this trick avoids escaping issues
+ return "((char) " + (int) ch + ")";
+ }
+ }
+ }
+
+ public static String coerceToCharacter(String s, boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "(Character) org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Character.class)";
+ } else {
+ if (s == null || s.length() == 0) {
+ return "new Character((char) 0)";
+ } else {
+ char ch = s.charAt(0);
+ // this trick avoids escaping issues
+ return "new Character((char) " + (int) ch + ")";
+ }
+ }
+ }
+
+ public static String coerceToPrimitiveDouble(String s,
+ boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToDouble(" + s + ")";
+ } else {
+ if (s == null || s.length() == 0)
+ return "(double) 0";
+ else
+ return Double.valueOf(s).toString();
+ }
+ }
+
+ public static String coerceToDouble(String s, boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "(Double) org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Double.class)";
+ } else {
+ if (s == null || s.length() == 0) {
+ return "new Double(0)";
+ } else {
+ // Detect format error at translation time
+ return "new Double(" + Double.valueOf(s).toString() + ")";
+ }
+ }
+ }
+
+ public static String coerceToPrimitiveFloat(String s,
+ boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToFloat(" + s + ")";
+ } else {
+ if (s == null || s.length() == 0)
+ return "(float) 0";
+ else
+ return Float.valueOf(s).toString() + "f";
+ }
+ }
+
+ public static String coerceToFloat(String s, boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "(Float) org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Float.class)";
+ } else {
+ if (s == null || s.length() == 0) {
+ return "new Float(0)";
+ } else {
+ // Detect format error at translation time
+ return "new Float(" + Float.valueOf(s).toString() + "f)";
+ }
+ }
+ }
+
+ public static String coerceToInt(String s, boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToInt(" + s + ")";
+ } else {
+ if (s == null || s.length() == 0)
+ return "0";
+ else
+ return Integer.valueOf(s).toString();
+ }
+ }
+
+ public static String coerceToInteger(String s, boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "(Integer) org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Integer.class)";
+ } else {
+ if (s == null || s.length() == 0) {
+ return "new Integer(0)";
+ } else {
+ // Detect format error at translation time
+ return "new Integer(" + Integer.valueOf(s).toString() + ")";
+ }
+ }
+ }
+
+ public static String coerceToPrimitiveShort(String s,
+ boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToShort(" + s + ")";
+ } else {
+ if (s == null || s.length() == 0)
+ return "(short) 0";
+ else
+ return "((short) " + Short.valueOf(s).toString() + ")";
+ }
+ }
+
+ public static String coerceToShort(String s, boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "(Short) org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Short.class)";
+ } else {
+ if (s == null || s.length() == 0) {
+ return "new Short((short) 0)";
+ } else {
+ // Detect format error at translation time
+ return "new Short(\"" + Short.valueOf(s).toString() + "\")";
+ }
+ }
+ }
+
+ public static String coerceToPrimitiveLong(String s,
+ boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerceToLong(" + s + ")";
+ } else {
+ if (s == null || s.length() == 0)
+ return "(long) 0";
+ else
+ return Long.valueOf(s).toString() + "l";
+ }
+ }
+
+ public static String coerceToLong(String s, boolean isNamedAttribute) {
+ if (isNamedAttribute) {
+ return "(Long) org.apache.struts2.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Long.class)";
+ } else {
+ if (s == null || s.length() == 0) {
+ return "new Long(0)";
+ } else {
+ // Detect format error at translation time
+ return "new Long(" + Long.valueOf(s).toString() + "l)";
+ }
+ }
+ }
+
+ public static InputStream getInputStream(String fname, JarFile jarFile,
+ JspCompilationContext ctxt,
+ ErrorDispatcher err)
+ throws JasperException, IOException {
+
+ InputStream in = null;
+
+ if (jarFile != null) {
+ String jarEntryName = fname.substring(1, fname.length());
+ ZipEntry jarEntry = jarFile.getEntry(jarEntryName);
+ if (jarEntry == null) {
+ err.jspError("jsp.error.file.not.found", fname);
+ }
+ in = jarFile.getInputStream(jarEntry);
+ } else {
+ in = ctxt.getResourceAsStream(fname);
+ }
+
+ if (in == null) {
+ err.jspError("jsp.error.file.not.found", fname);
+ }
+
+ return in;
+ }
+
+ /**
+ * Gets the fully-qualified class name of the tag handler corresponding to
+ * the given tag file path.
+ *
+ * @param path Tag file path
+ * @param err Error dispatcher
+ *
+ * @return Fully-qualified class name of the tag handler corresponding to
+ * the given tag file path
+ */
+ public static String getTagHandlerClassName(String path,
+ ErrorDispatcher err)
+ throws JasperException {
+
+ String className = null;
+ int begin = 0;
+ int index;
+
+ index = path.lastIndexOf(".tag");
+ if (index == -1) {
+ err.jspError("jsp.error.tagfile.badSuffix", path);
+ }
+
+ //It's tempting to remove the ".tag" suffix here, but we can't.
+ //If we remove it, the fully-qualified class name of this tag
+ //could conflict with the package name of other tags.
+ //For instance, the tag file
+ // /WEB-INF/tags/foo.tag
+ //would have fully-qualified class name
+ // org.apache.jsp.tag.web.foo
+ //which would conflict with the package name of the tag file
+ // /WEB-INF/tags/foo/bar.tag
+
+ index = path.indexOf(WEB_INF_TAGS);
+ if (index != -1) {
+ className = "org.apache.jsp.tag.web.";
+ begin = index + WEB_INF_TAGS.length();
+ } else {
+ index = path.indexOf(META_INF_TAGS);
+ if (index != -1) {
+ className = "org.apache.jsp.tag.meta.";
+ begin = index + META_INF_TAGS.length();
+ } else {
+ err.jspError("jsp.error.tagfile.illegalPath", path);
+ }
+ }
+
+ className += makeJavaPackage(path.substring(begin));
+
+ return className;
+ }
+
+ /**
+ * Converts the given path to a Java package or fully-qualified class name
+ *
+ * @param path Path to convert
+ *
+ * @return Java package corresponding to the given path
+ */
+ public static final String makeJavaPackage(String path) {
+ String classNameComponents[] = split(path,"/");
+ StringBuffer legalClassNames = new StringBuffer();
+ for (int i = 0; i < classNameComponents.length; i++) {
+ legalClassNames.append(makeJavaIdentifier(classNameComponents[i]));
+ if (i < classNameComponents.length - 1) {
+ legalClassNames.append('.');
+ }
+ }
+ return legalClassNames.toString();
+ }
+
+ /**
+ * Splits a string into it's components.
+ * @param path String to split
+ * @param pat Pattern to split at
+ * @return the components of the path
+ */
+ private static final String [] split(String path, String pat) {
+ Vector comps = new Vector();
+ int pos = path.indexOf(pat);
+ int start = 0;
+ while( pos >= 0 ) {
+ if(pos > start ) {
+ String comp = path.substring(start,pos);
+ comps.add(comp);
+ }
+ start = pos + pat.length();
+ pos = path.indexOf(pat,start);
+ }
+ if( start < path.length()) {
+ comps.add(path.substring(start));
+ }
+ String [] result = new String[comps.size()];
+ for(int i=0; i < comps.size(); i++) {
+ result[i] = (String)comps.elementAt(i);
+ }
+ return result;
+ }
+
+ /**
+ * Converts the given identifier to a legal Java identifier
+ *
+ * @param identifier Identifier to convert
+ *
+ * @return Legal Java identifier corresponding to the given identifier
+ */
+ public static final String makeJavaIdentifier(String identifier) {
+ StringBuffer modifiedIdentifier =
+ new StringBuffer(identifier.length());
+ if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
+ modifiedIdentifier.append('_');
+ }
+ for (int i = 0; i < identifier.length(); i++) {
+ char ch = identifier.charAt(i);
+ if (Character.isJavaIdentifierPart(ch) && ch != '_') {
+ modifiedIdentifier.append(ch);
+ } else if (ch == '.') {
+ modifiedIdentifier.append('_');
+ } else {
+ modifiedIdentifier.append(mangleChar(ch));
+ }
+ }
+ if (isJavaKeyword(modifiedIdentifier.toString())) {
+ modifiedIdentifier.append('_');
+ }
+ return modifiedIdentifier.toString();
+ }
+
+ /**
+ * Mangle the specified character to create a legal Java class name.
+ */
+ public static final String mangleChar(char ch) {
+ char[] result = new char[5];
+ result[0] = '_';
+ result[1] = Character.forDigit((ch >> 12) & 0xf, 16);
+ result[2] = Character.forDigit((ch >> 8) & 0xf, 16);
+ result[3] = Character.forDigit((ch >> 4) & 0xf, 16);
+ result[4] = Character.forDigit(ch & 0xf, 16);
+ return new String(result);
+ }
+
+ /**
+ * Test whether the argument is a Java keyword
+ */
+ public static boolean isJavaKeyword(String key) {
+ int i = 0;
+ int j = javaKeywords.length;
+ while (i < j) {
+ int k = (i+j)/2;
+ int result = javaKeywords[k].compareTo(key);
+ if (result == 0) {
+ return true;
+ }
+ if (result < 0) {
+ i = k+1;
+ } else {
+ j = k;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Converts the given Xml name to a legal Java identifier. This is
+ * slightly more efficient than makeJavaIdentifier in that we only need
+ * to worry about '.', '-', and ':' in the string. We also assume that
+ * the resultant string is further concatenated with some prefix string
+ * so that we don't have to worry about it being a Java key word.
+ *
+ * @param name Identifier to convert
+ *
+ * @return Legal Java identifier corresponding to the given identifier
+ */
+ public static final String makeXmlJavaIdentifier(String name) {
+ if (name.indexOf('-') >= 0)
+ name = replace(name, '-', "$1");
+ if (name.indexOf('.') >= 0)
+ name = replace(name, '.', "$2");
+ if (name.indexOf(':') >= 0)
+ name = replace(name, ':', "$3");
+ return name;
+ }
+
+ static InputStreamReader getReader(String fname, String encoding,
+ JarFile jarFile,
+ JspCompilationContext ctxt,
+ ErrorDispatcher err)
+ throws JasperException, IOException {
+
+ InputStreamReader reader = null;
+ InputStream in = getInputStream(fname, jarFile, ctxt, err);
+
+ try {
+ reader = new InputStreamReader(in, encoding);
+ } catch (UnsupportedEncodingException ex) {
+ err.jspError("jsp.error.unsupported.encoding", encoding);
+ }
+
+ return reader;
+ }
+
+ /**
+ * Class.getName() return arrays in the form "[[[<et>", where et,
+ * the element type can be one of ZBCDFIJS or L<classname>;
+ * It is converted into forms that can be understood by javac.
+ */
+ public static String toJavaSourceType(String type) {
+
+ if (type.charAt(0) != '[') {
+ return type;
+ }
+
+ int dims = 1;
+ String t = null;
+ for (int i = 1; i < type.length(); i++) {
+ if (type.charAt(i) == '[') {
+ dims++;
+ } else {
+ switch (type.charAt(i)) {
+ case 'Z': t = "boolean"; break;
+ case 'B': t = "byte"; break;
+ case 'C': t = "char"; break;
+ case 'D': t = "double"; break;
+ case 'F': t = "float"; break;
+ case 'I': t = "int"; break;
+ case 'J': t = "long"; break;
+ case 'S': t = "short"; break;
+ case 'L': t = type.substring(i+1, type.indexOf(';')); break;
+ }
+ break;
+ }
+ }
+ StringBuffer resultType = new StringBuffer(t);
+ for (; dims > 0; dims--) {
+ resultType.append("[]");
+ }
+ return resultType.toString();
+ }
+
+ /**
+ * Compute the canonical name from a Class instance. Note that a
+ * simple replacment of '$' with '.' of a binary name would not work,
+ * as '$' is a legal Java Identifier character.
+ * @param c A instance of java.lang.Class
+ * @return The canonical name of c.
+ */
+ public static String getCanonicalName(Class c) {
+
+ String binaryName = c.getName();
+ c = c.getDeclaringClass();
+
+ if (c == null) {
+ return binaryName;
+ }
+
+ StringBuffer buf = new StringBuffer(binaryName);
+ do {
+ buf.setCharAt(c.getName().length(), '.');
+ c = c.getDeclaringClass();
+ } while ( c != null);
+
+ return buf.toString();
+ }
+}
+
Added: struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Localizer.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Localizer.java?rev=799681&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Localizer.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/Localizer.java Fri Jul 31 18:12:48 2009
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.struts2.jasper.compiler;
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Class responsible for converting error codes to corresponding localized
+ * error messages.
+ *
+ * @author Jan Luehe
+ */
+public class Localizer {
+
+ private static ResourceBundle bundle = null;
+
+ static {
+ try {
+ bundle = ResourceBundle.getBundle(
+ "org.apache.struts2.jasper.resources.LocalStrings");
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ /*
+ * Returns the localized error message corresponding to the given error
+ * code.
+ *
+ * If the given error code is not defined in the resource bundle for
+ * localized error messages, it is used as the error message.
+ *
+ * @param errCode Error code to localize
+ *
+ * @return Localized error message
+ */
+ public static String getMessage(String errCode) {
+ String errMsg = errCode;
+ try {
+ errMsg = bundle.getString(errCode);
+ } catch (MissingResourceException e) {
+ }
+ return errMsg;
+ }
+
+ /*
+ * Returns the localized error message corresponding to the given error
+ * code.
+ *
+ * If the given error code is not defined in the resource bundle for
+ * localized error messages, it is used as the error message.
+ *
+ * @param errCode Error code to localize
+ * @param arg Argument for parametric replacement
+ *
+ * @return Localized error message
+ */
+ public static String getMessage(String errCode, String arg) {
+ return getMessage(errCode, new Object[] {arg});
+ }
+
+ /*
+ * Returns the localized error message corresponding to the given error
+ * code.
+ *
+ * If the given error code is not defined in the resource bundle for
+ * localized error messages, it is used as the error message.
+ *
+ * @param errCode Error code to localize
+ * @param arg1 First argument for parametric replacement
+ * @param arg2 Second argument for parametric replacement
+ *
+ * @return Localized error message
+ */
+ public static String getMessage(String errCode, String arg1, String arg2) {
+ return getMessage(errCode, new Object[] {arg1, arg2});
+ }
+
+ /*
+ * Returns the localized error message corresponding to the given error
+ * code.
+ *
+ * If the given error code is not defined in the resource bundle for
+ * localized error messages, it is used as the error message.
+ *
+ * @param errCode Error code to localize
+ * @param arg1 First argument for parametric replacement
+ * @param arg2 Second argument for parametric replacement
+ * @param arg3 Third argument for parametric replacement
+ *
+ * @return Localized error message
+ */
+ public static String getMessage(String errCode, String arg1, String arg2,
+ String arg3) {
+ return getMessage(errCode, new Object[] {arg1, arg2, arg3});
+ }
+
+ /*
+ * Returns the localized error message corresponding to the given error
+ * code.
+ *
+ * If the given error code is not defined in the resource bundle for
+ * localized error messages, it is used as the error message.
+ *
+ * @param errCode Error code to localize
+ * @param arg1 First argument for parametric replacement
+ * @param arg2 Second argument for parametric replacement
+ * @param arg3 Third argument for parametric replacement
+ * @param arg4 Fourth argument for parametric replacement
+ *
+ * @return Localized error message
+ */
+ public static String getMessage(String errCode, String arg1, String arg2,
+ String arg3, String arg4) {
+ return getMessage(errCode, new Object[] {arg1, arg2, arg3, arg4});
+ }
+
+ /*
+ * Returns the localized error message corresponding to the given error
+ * code.
+ *
+ * If the given error code is not defined in the resource bundle for
+ * localized error messages, it is used as the error message.
+ *
+ * @param errCode Error code to localize
+ * @param args Arguments for parametric replacement
+ *
+ * @return Localized error message
+ */
+ public static String getMessage(String errCode, Object[] args) {
+ String errMsg = errCode;
+ try {
+ errMsg = bundle.getString(errCode);
+ if (args != null) {
+ MessageFormat formatter = new MessageFormat(errMsg);
+ errMsg = formatter.format(args);
+ }
+ } catch (MissingResourceException e) {
+ }
+
+ return errMsg;
+ }
+}