diff --git a/org.zkoss.zk.library/.classpath b/org.zkoss.zk.library/.classpath
index 7cc726e45d..38fd841f21 100644
--- a/org.zkoss.zk.library/.classpath
+++ b/org.zkoss.zk.library/.classpath
@@ -24,7 +24,7 @@
-
+
diff --git a/org.zkoss.zk.library/src/org/zkoss/web/util/resource/ResourceCaches.java b/org.zkoss.zk.library/src/org/zkoss/web/util/resource/ResourceCaches.java
new file mode 100644
index 0000000000..f8682e22bb
--- /dev/null
+++ b/org.zkoss.zk.library/src/org/zkoss/web/util/resource/ResourceCaches.java
@@ -0,0 +1,156 @@
+/* ResourceCaches.java
+
+ Purpose:
+
+ Description:
+
+ History:
+ Tue Aug 30 18:31:05 2005, Created by tomyeh
+
+Copyright (C) 2005 Potix Corporation. All Rights Reserved.
+
+{{IS_RIGHT
+ This program is distributed under LGPL Version 2.1 in the hope that
+ it will be useful, but WITHOUT ANY WARRANTY.
+}}IS_RIGHT
+*/
+package org.zkoss.web.util.resource;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.servlet.ServletContext;
+
+import org.zkoss.lang.SystemException;
+import org.zkoss.util.logging.Log;
+import org.zkoss.web.servlet.Servlets;
+
+/**
+ * Utilities to load (and parse) the Servlet resource.
+ * Notice that {@link ResourceCache} and {@link ResourceLoader}
+ * must be used rather than
+ * {@link org.zkoss.util.resource.ResourceCache}
+ * and {@link org.zkoss.util.resource.Loader}.
+ *
+ *
Usage:
+ *
+ * - Implements a loader by extending from {@link ResourceLoader}.
+ * - Creates a resource cache ({@link ResourceCache})
+ * by use of the loader in the previous step.
+ * - Invoke {@link #get} to load the resource.
+ *
+ *
+ * @author tomyeh
+ */
+public class ResourceCaches {
+ private static final Log log = Log.lookup(ResourceCaches.class);
+
+ /** Loads, parses and returns the resource of the specified URI,
+ * or null if not found. The parser is defined by the loader defined
+ * in {@link ResourceCache}.
+ *
+ * @param cache the resource cache.
+ * Note: its loader must extend from {@link ResourceLoader}.
+ * @param path the URI path
+ * @param extra the extra parameter that will be passed to
+ * {@link ResourceLoader#parse(String,File,Object)} and
+ * {@link ResourceLoader#parse(String,URL,Object)}
+ */
+ public static final
+ V get(ResourceCache cache, ServletContext ctx, String path, Object extra) {
+ //20050905: Tom Yeh
+ //We don't need to handle the default name if user specifies only a dir
+ //because it is handled by the container directlys
+ //And, web developer has to specify in web.xml
+ URL url = null;
+ if (path == null || path.length() == 0) path = "/";
+ else if (path.charAt(0) != '/') {
+ if (path.indexOf("://") > 0) {
+ try {
+ url = new URL(path);
+ } catch (java.net.MalformedURLException ex) {
+ throw new SystemException(ex);
+ }
+ }else path = '/' + path;
+ }
+
+ if (url == null) {
+ if (path.startsWith("/~")) {
+ final ServletContext ctx0 = ctx;
+ final String path0 = path;
+ final int j = path.indexOf('/', 2);
+ final String ctxpath;
+ if (j >= 0) {
+ ctxpath = "/" + path.substring(2, j);
+ path = path.substring(j);
+ } else {
+ ctxpath = "/" + path.substring(2);
+ path = "/";
+ }
+
+ final ExtendletContext extctx =
+ Servlets.getExtendletContext(ctx, ctxpath.substring(1));
+ if (extctx != null) {
+ url = extctx.getResource(path);
+// if (log.debugable()) log.debug("Resolving "+path0+" to "+url);
+ if (url == null)
+ return null;
+ try {
+ return cache.get(new ResourceInfo(path, url, extra));
+ } catch (Throwable ex) {
+ final IOException ioex = getIOException(ex);
+ if (ioex == null)
+ throw SystemException.Aide.wrap(ex);
+ log.warningBriefly("Unable to load "+url, ioex);
+ }
+ return null;
+ }
+
+ ctx = ctx.getContext(ctxpath);
+ if (ctx == null) { //failed
+// if (log.debugable()) log.debug("Context not found: "+ctxpath);
+ ctx = ctx0; path = path0;//restore
+ }
+ }
+
+ final String flnm = ctx.getRealPath(path);
+ if (flnm != null) {
+ File file = new File(flnm);
+ if (file.exists()) {
+ try {
+ return cache.get(new ResourceInfo(path, file, extra));
+ //it is loader's job to check the existence
+ } catch (Throwable ex) {
+ final IOException ioex = getIOException(ex);
+ if (ioex == null)
+ throw SystemException.Aide.wrap(ex);
+ log.warningBriefly("Unable to load "+flnm, ioex);
+ }
+ return null;
+ }
+ }
+ }
+
+ //try url because some server uses JAR format
+ try {
+ if (url == null)
+ url = ctx.getResource(path);
+ if (url != null)
+ return cache.get(new ResourceInfo(path, url, extra));
+ } catch (Throwable ex) {
+ final IOException ioex = getIOException(ex);
+ if (ioex == null)
+ throw SystemException.Aide.wrap(ex);
+ log.warningBriefly("Unable to load "+path, ioex);
+ }
+ return null;
+ }
+ //don't eat exceptions other than IOException
+ private static IOException getIOException(Throwable ex) {
+ for (; ex != null; ex = ex.getCause())
+ if (ex instanceof IOException)
+ return (IOException)ex;
+ return null;
+ }
+}