Esempio n. 1
0
/**
 * Utilities to clean up resources when the application is stopping. Currenly, when a ZK application
 * is stopping, ZK will invoke {@link #cleanup}. Thus you can register an implementation of {@link
 * Cleanup} to release the resources.
 *
 * <p>Notice that this utility is introduced mainly to resolve the memory lead issue if an
 * application is hot re-deployed. A typical example is to stop any pooled threads.
 *
 * <h3>How to use:</h3>
 *
 * <p>First, register the cleanup with {@link #add}.
 *
 * <p>Second, invoke {@link #cleanup} when necessary, such as when the application is stopping.
 *
 * @author tomyeh
 * @since 3.6.5
 */
public class Cleanups {
  private static final Log log = Log.lookup(Cleanups.class);
  private static final List _cleanups = new LinkedList();

  /**
   * Registers a cleanup.
   *
   * @return true if it is added successfully, or false if the cleanup has been regsitered before.
   */
  public static boolean add(Cleanup cleanup) {
    if (cleanup == null) throw new IllegalArgumentException();

    synchronized (_cleanups) {
      if (_cleanups.contains(cleanup)) return false;
      return _cleanups.add(cleanup);
    }
  }
  /**
   * Un-registers a cleanup.
   *
   * @return true if it is removed successfully, or false if not registered before.
   */
  public static boolean remove(Cleanup cleanup) {
    synchronized (_cleanups) {
      return _cleanups.remove(cleanup);
    }
  }

  /** Invokes all cleanups registered with {@link #add}. */
  public static void cleanup() {
    final List cleanups;
    synchronized (_cleanups) {
      cleanups = new ArrayList(_cleanups);
    }

    for (Iterator it = cleanups.iterator(); it.hasNext(); ) {
      final Cleanup cleanup = (Cleanup) it.next();
      try {
        cleanup.cleanup();
      } catch (Throwable ex) {
        log.error("Failed to invoke " + cleanup);
      }
    }
  }
  /**
   * The interface to implement for each cleanup. It is used with {@link Cleanups#add}.
   *
   * @since 3.6.5
   */
  public static interface Cleanup {
    /** Cleanups the thread local variables. */
    public void cleanup();
  }
}
Esempio n. 2
0
/**
 * An implementation of {@link RequestQueue} behaving as a queue of {@link AuRequest}. There is one
 * queue for each desktop.
 *
 * <p>Implementation Note: Unlike only of desktop members, this class must be thread-safe.
 *
 * @author tomyeh
 */
public class RequestQueueImpl implements RequestQueue {
  private static final Log log = Log.lookup(RequestQueueImpl.class);

  /** A list of pending {@link AuRequest}. */
  private final List _requests = new LinkedList();
  /** A list of request ID for performance measurement. */
  private List _pfreqIds;

  // -- RequestQueue --//
  public void addPerfRequestId(String requestId) {
    if (_pfreqIds == null) _pfreqIds = new LinkedList();
    _pfreqIds.add(requestId);
  }

  public Collection clearPerfRequestIds() {
    final List old = _pfreqIds;
    _pfreqIds = null;
    return old;
  }

  public boolean isEmpty() {
    while (!_requests.isEmpty()) {
      if (isObsolete((AuRequest) _requests.get(0))) _requests.remove(0);
      else return false;
    }
    return true;
  }

  private static boolean isObsolete(AuRequest request) {
    final Component comp = request.getComponent();
    if (comp != null) {
      final Desktop dt = comp.getDesktop();
      return dt != null && dt != request.getDesktop();
      // Since 3.5.0, we allow a component to process events even
      // if it is detached (due to implementation of EventQueue)
    }
    return false;
  }

  public AuRequest nextRequest() {
    while (!_requests.isEmpty()) {
      final AuRequest request = (AuRequest) _requests.remove(0);
      if (!isObsolete(request)) return request;
    }
    return null;
  }

  public void addRequests(Collection requests) {
    for (Iterator it = requests.iterator(); it.hasNext(); ) {
      final AuRequest request = (AuRequest) it.next();
      try {
        request.activate();
        if (!isObsolete(request)) addRequest(request);
      } catch (ComponentNotFoundException ex) { // ignore it
        // ignore it since a long request might remove a timer
        // while clients already queues onTimer generated by the timer
        if (log.debugable()) log.debug("Ignore request: " + ex.getMessage());
      }
    }
  }

  private void addRequest(AuRequest request) {
    // case 1, BUSY_IGNORE: Drop any existent ignorable requests
    // We don't need to iterate all because requests is added one-by-one
    // In other words, if any temporty request, it must be the last
    {
      int last = _requests.size() - 1;
      if (last < 0) { // optimize the most common case
        _requests.add(request);
        return;
      }

      final AuRequest req2 = (AuRequest) _requests.get(last);
      if ((req2.getOptions() & AuRequest.BUSY_IGNORE) != 0) {
        _requests.remove(last); // drop it
        if (last == 0) {
          _requests.add(request);
          return;
        }
      }
    }

    final String name = request.getCommand();
    final int opts = request.getOptions();

    // Since 3.0.2, redundant CTRL_GROUP is removed at the client

    // case 2, DUPLICATE_IGNORE: drop existent request if they are the same
    // as the arrival.
    if ((opts & AuRequest.DUPLICATE_IGNORE) != 0) {
      final String uuid = getUuid(request);
      for (Iterator it = _requests.iterator(); it.hasNext(); ) {
        final AuRequest req2 = (AuRequest) it.next();
        if (req2.getCommand().equals(name) && Objects.equals(getUuid(req2), uuid)) {
          it.remove(); // drop req2 (the old one)
          break; // no need to iterate because impossible to have more
        }
      }

      // Case 3, REPEAT_IGNORE: drop existent if the immediate
      // following is the same
    } else if ((opts & AuRequest.REPEAT_IGNORE) != 0) {
      final int last = _requests.size() - 1;
      final AuRequest req2 = (AuRequest) _requests.get(last);
      if (req2.getCommand().equals(name) && Objects.equals(getUuid(req2), getUuid(request))) {
        _requests.remove(last);
      }
    }

    _requests.add(request);
  }

  private String getUuid(AuRequest request) {
    final Component comp = request.getComponent();
    if (comp != null) return comp.getUuid();
    final Page page = request.getPage();
    if (page != null) return page.getUuid();
    return null;
  }
}
/**
 * Listener to make sure each ZK thread got the same ThreadLocal value of the spring's
 * org.springframework.transaction.support.TransactionSynchronizationManager; especially those
 * thread bound resources.
 *
 * <p>This listener is used with Spring Framework (version 1.2.8+) "thread" bounded resources.
 *
 * <pre><code>
 * 	&lt;listener>
 * 	&lt;description>Spring TransactionSynchronizationManager handler&lt;/description>
 * 	&lt;listener-class>org.zkoss.zkplus.spring.SpringTransactionSynchronizationListener&lt;/listener-class>
 * &lt;/listener>
 * </code></pre>
 *
 * <p>Applicable to Spring Framework version 2.x or later
 *
 * @author henrichen
 */
public class SpringTransactionSynchronizationListener
    implements EventThreadInit, EventThreadCleanup, EventThreadResume {
  private static final Log log = Log.lookup(SpringTransactionSynchronizationListener.class);

  private Object[] _threadLocals = null;
  private final boolean _enabled; // whether event thread enabled

  public SpringTransactionSynchronizationListener() {
    final WebApp app = Executions.getCurrent().getDesktop().getWebApp();
    _enabled = app.getConfiguration().isEventThreadEnabled();
  }

  // -- EventThreadInit --//
  public void prepare(Component comp, Event evt) {
    if (_enabled) {
      getThreadLocals(); // get from servlet thread's ThreadLocal
    }
  }

  public boolean init(Component comp, Event evt) {
    if (_enabled) {
      setThreadLocals(); // copy to event thread's ThreadLocal
    }
    return true;
  }

  // -- EventThreadCleanup --//
  public void cleanup(Component comp, Event evt, List errs) {
    if (_enabled) {
      getThreadLocals(); // get from event thread's ThreadLocal
      // we don't handle the exception since the ZK engine will throw it again!
    }
  }

  public void complete(Component comp, Event evt) {
    if (_enabled) {
      setThreadLocals(); // copy to servlet thread's ThreadLocal
    }
  }

  // -- EventThreadResume --//
  public void beforeResume(Component comp, Event evt) {
    if (_enabled) {
      getThreadLocals(); // get from servlet thread's ThreadLocal
    }
  }

  public void afterResume(Component comp, Event evt) {
    if (_enabled) {
      setThreadLocals(); // copy to event thread's ThreadLocal
    }
  }

  public void abortResume(Component comp, Event evt) {
    // do nothing
  }

  // -- utilities --//
  private void getThreadLocals() {
    try {
      Class cls =
          Classes.forNameByThread(
              "org.springframework.transaction.support.TransactionSynchronizationManager");

      _threadLocals = new Object[7];
      _threadLocals[0] = getThreadLocal(cls, "resources").get();
      _threadLocals[1] = getThreadLocal(cls, "synchronizations").get();
      _threadLocals[2] = getThreadLocal(cls, "currentTransactionName").get();
      _threadLocals[3] = getThreadLocal(cls, "currentTransactionReadOnly").get();
      _threadLocals[4] = getThreadLocal(cls, "actualTransactionActive").get();

      // 20070907, Henri Chen: bug 1785457, hibernate3 might not used
      try {
        cls = Classes.forNameByThread("org.springframework.orm.hibernate3.SessionFactoryUtils");
        _threadLocals[5] = getThreadLocal(cls, "deferredCloseHolder").get();
      } catch (ClassNotFoundException ex) {
        // ignore if hibernate 3 is not used.
      }

      cls =
          Classes.forNameByThread(
              "org.springframework.transaction.interceptor.TransactionAspectSupport");

      // Spring 1.2.8 and Spring 2.0.x, the ThreadLocal field name has changed, default use 2.0.x
      // 2.0.x transactionInfoHolder
      // 1.2.8 currentTransactionInfo
      try {
        _threadLocals[6] = getThreadLocal(cls, "transactionInfoHolder").get();
      } catch (SystemException ex) {
        if (ex.getCause() instanceof NoSuchFieldException) {
          _threadLocals[6] = getThreadLocal(cls, "currentTransactionInfo").get();
        } else {
          throw ex;
        }
      }
    } catch (ClassNotFoundException ex) {
      throw UiException.Aide.wrap(ex);
    }
  }

  @SuppressWarnings("unchecked")
  private void setThreadLocals() {
    if (_threadLocals != null) {
      try {
        Class cls =
            Classes.forNameByThread(
                "org.springframework.transaction.support.TransactionSynchronizationManager");

        getThreadLocal(cls, "resources").set(_threadLocals[0]);
        getThreadLocal(cls, "synchronizations").set(_threadLocals[1]);
        getThreadLocal(cls, "currentTransactionName").set(_threadLocals[2]);
        getThreadLocal(cls, "currentTransactionReadOnly").set(_threadLocals[3]);
        getThreadLocal(cls, "actualTransactionActive").set(_threadLocals[4]);

        // 20070907, Henri Chen: bug 1785457, hibernate3 might not used
        try {
          cls = Classes.forNameByThread("org.springframework.orm.hibernate3.SessionFactoryUtils");
          getThreadLocal(cls, "deferredCloseHolder").set(_threadLocals[5]);
        } catch (ClassNotFoundException ex) {
          // ignore if hibernate 3 is not used.
        }

        cls =
            Classes.forNameByThread(
                "org.springframework.transaction.interceptor.TransactionAspectSupport");
        // Spring 1.2.8 and Spring 2.0.x, the ThreadLocal field name has changed, default use 2.0.x
        // 2.0.x transactionInfoHolder
        // 1.2.8 currentTransactionInfo
        try {
          getThreadLocal(cls, "transactionInfoHolder").set(_threadLocals[6]);
        } catch (SystemException ex) {
          if (ex.getCause() instanceof NoSuchFieldException) {
            getThreadLocal(cls, "currentTransactionInfo").set(_threadLocals[6]);
          } else {
            throw ex;
          }
        }

        _threadLocals = null;
      } catch (ClassNotFoundException ex) {
        throw UiException.Aide.wrap(ex);
      }
    }
  }

  private ThreadLocal getThreadLocal(Class cls, String fldname) {
    return ThreadLocals.getThreadLocal(cls, fldname);
  }
}
Esempio n. 4
0
/**
 * Skeletal implementation for {@link WpdExtendlet} and {@link WcsExtendlet}.
 *
 * @author tomyeh
 * @since 5.0.0
 */
/*package*/ abstract class AbstractExtendlet<V> implements Extendlet {
  static final Log log = Log.lookup(AbstractExtendlet.class);

  ExtendletContext _webctx;
  /** DSP interpretation cache. */
  ResourceCache<String, V> _cache;
  /** The provider. */
  private ThreadLocal<Provider> _provider = new ThreadLocal<Provider>();

  private Boolean _debugJS;

  // Public Utilities
  /** Sets whether to generate JS files that is easy to debug. */
  public void setDebugJS(boolean debugJS) {
    _debugJS = Boolean.valueOf(debugJS);
    if (_cache != null) _cache.clear();
  }
  /** Returns whether to generate JS files that is easy to debug. */
  public boolean isDebugJS() {
    if (_debugJS == null) {
      final WebApp wapp = getWebApp();
      if (wapp == null) return true; // zk lighter
      _debugJS = Boolean.valueOf(wapp.getConfiguration().isDebugJS());
    }
    return _debugJS.booleanValue();
  }

  // Package Utilities
  Provider getProvider() {
    return _provider.get();
  }

  void setProvider(Provider provider) {
    _provider.set(provider);
  }

  WebApp getWebApp() {
    return _webctx != null
        ? WebManager.getWebManager(_webctx.getServletContext()).getWebApp()
        : null;
  }

  ServletContext getServletContext() {
    return _webctx != null ? _webctx.getServletContext() : null;
  }

  void init(ExtendletConfig config, ExtendletLoader<V> loader) {
    _webctx = config.getExtendletContext();
    _cache = new ResourceCache<String, V>(loader, 16);
    _cache.setMaxSize(1024);
    _cache.setLifetime(60 * 60 * 1000); // 1hr
    final int checkPeriod = loader.getCheckPeriod();
    _cache.setCheckPeriod(checkPeriod >= 0 ? checkPeriod : 60 * 60 * 1000); // 1hr
  }

  /** Returns the static method defined in an element, or null if failed. */
  /*package*/ static MethodInfo getMethodInfo(Element el) {
    final String clsnm = IDOMs.getRequiredAttributeValue(el, "class");
    final String sig = IDOMs.getRequiredAttributeValue(el, "signature");
    final Class cls;
    try {
      cls = Classes.forNameByThread(clsnm);
    } catch (ClassNotFoundException ex) {
      log.error("Class not found: " + clsnm + ", " + el.getLocator());
      return null; // to report as many errors as possible
    }

    try {
      final Method mtd = Classes.getMethodBySignature(cls, sig, null);
      if ((mtd.getModifiers() & Modifier.STATIC) == 0) {
        log.error("Not a static method: " + mtd);
        return null;
      }

      final Object[] args = new Object[mtd.getParameterTypes().length];
      for (int j = 0; j < args.length; ++j) args[j] = el.getAttributeValue("arg" + j);

      return new MethodInfo(mtd, args);
    } catch (ClassNotFoundException ex) {
      log.realCauseBriefly(
          "Unable to load class when resolving " + sig + " " + el.getLocator(), ex);
    } catch (NoSuchMethodException ex) {
      log.error("Method not found in " + clsnm + ": " + sig + " " + el.getLocator());
    }
    return null;
  }
  /** Invokes a static method. */
  /*package*/ String invoke(MethodInfo mi) {
    final Provider provider = getProvider();
    final Class[] argTypes = mi.method.getParameterTypes();
    final Object[] args = mi.arguments;
    if (provider != null)
      for (int j = 0; j < args.length; ++j)
        if (ServletRequest.class.isAssignableFrom(argTypes[j])) args[j] = provider.request;
        else if (ServletResponse.class.isAssignableFrom(argTypes[j])) args[j] = provider.response;
        else if (ServletContext.class.isAssignableFrom(argTypes[j])) args[j] = getServletContext();
    try {
      Object o = mi.method.invoke(null, args);
      return o instanceof String ? (String) o : "";
    } catch (Throwable ex) { // log and eat ex
      log.error("Unable to invoke " + mi.method, ex);
      return "";
    }
  }

  // Extendlet
  public boolean getFeature(int feature) {
    return feature == ALLOW_DIRECT_INCLUDE;
  }

  /*package*/
  // called by Provider
  InputStream getResourceAsStream(HttpServletRequest request, String path, boolean locate)
      throws IOException, ServletException {
    if (locate)
      path = Servlets.locate(_webctx.getServletContext(), request, path, _webctx.getLocator());

    if (_cache.getCheckPeriod() >= 0) {
      // Due to Web server might cache the result, we use URL if possible
      try {
        URL url = _webctx.getResource(path);
        if (url != null) return url.openStream();
      } catch (Throwable ex) {
        log.warningBriefly("Unable to read from URL: " + path, ex);
      }
    }

    // Note: _webctx will handle the renaming for debugJS (.src.js)
    return _webctx.getResourceAsStream(path);
  }
  /*package*/
  // called by Provider
  URL getResource(String path) throws IOException {
    return _webctx.getResource(path);
  }

  // utility class
  /*package*/ static class MethodInfo {
    final Method method;
    final Object[] arguments;

    MethodInfo(Method method, Object[] arguments) {
      this.method = method;
      this.arguments = arguments;
    }
  }
}
Esempio n. 5
0
/**
 * Used to receive command from the server and send result back to client. Though it is called
 * DHtmlUpdateServlet, it is used to serve all kind of HTTP-based clients, including ajax
 * (HTML+Ajax), mil (Mobile Interactive Language), and others (see {@link Desktop#getDeviceType}.
 *
 * <p>Init parameters:
 *
 * <dl>
 *   <dt>compress
 *   <dd>It specifies whether to compress the output if the browser supports the compression
 *       (Accept-Encoding).
 *   <dt>extension0, extension1...
 *   <dd>It specifies an AU extension ({@link AuExtension}). The <code>extension0</code> parameter
 *       specifies the first AU extension, the <code>extension1</code> parameter the second AU
 *       extension, and so on.<br>
 *       The syntax of the value is<br>
 *       <code>/prefix=class</code>
 * </dl>
 *
 * <p>By default: there are two extensions are associated with "/upload" and "/view" (see {@link
 * #addAuExtension}. Also, "/web" is reserved. Don't associate to any AU extension.
 *
 * @author tomyeh
 */
public class DHtmlUpdateServlet extends HttpServlet {
  private static final Log log = Log.lookup(DHtmlUpdateServlet.class);
  private static final String ATTR_UPDATE_SERVLET = "org.zkoss.zk.au.http.updateServlet";
  private static final String ATTR_AU_PROCESSORS = "org.zkoss.zk.au.http.auProcessors";

  private long _lastModified;
  /** (String name, AuExtension). */
  private Map _aues = new HashMap(8);

  private boolean _compress = true;

  /**
   * Returns the update servlet of the specified application, or null if not loaded yet. Note: if
   * the update servlet is not loaded, it returns null.
   *
   * @since 3.0.2
   */
  public static DHtmlUpdateServlet getUpdateServlet(WebApp wapp) {
    return (DHtmlUpdateServlet)
        ((ServletContext) wapp.getNativeContext()).getAttribute(ATTR_UPDATE_SERVLET);
  }

  // Servlet//
  public void init() throws ServletException {
    final ServletConfig config = getServletConfig();
    final ServletContext ctx = getServletContext();
    ctx.setAttribute(ATTR_UPDATE_SERVLET, this);

    final WebManager webman = WebManager.getWebManager(ctx);
    String param = config.getInitParameter("compress");
    _compress = param == null || param.length() == 0 || "true".equals(param);
    if (!_compress) webman.getClassWebResource().setCompress(null); // disable all

    // Copies au extensions defined before DHtmlUpdateServlet is started
    final WebApp wapp = webman.getWebApp();
    final Map aues = (Map) wapp.getAttribute(ATTR_AU_PROCESSORS);
    if (aues != null) {
      for (Iterator it = aues.entrySet().iterator(); it.hasNext(); ) {
        final Map.Entry me = (Map.Entry) it.next();
        addAuExtension((String) me.getKey(), (AuExtension) me.getValue());
      }
      wapp.removeAttribute(ATTR_AU_PROCESSORS);
    }

    // ZK 5: extension defined in init-param has the higher priority
    for (int j = 0; ; ++j) {
      param = config.getInitParameter("extension" + j);
      if (param == null) {
        param = config.getInitParameter("processor" + j); // backward compatible
        if (param == null) break;
      }
      final int k = param.indexOf('=');
      if (k < 0) {
        log.warning("Ignore init-param: illegal format, " + param);
        continue;
      }

      final String prefix = param.substring(0, k).trim();
      final String clsnm = param.substring(k + 1).trim();
      try {
        addAuExtension(prefix, (AuExtension) Classes.newInstanceByThread(clsnm));
      } catch (ClassNotFoundException ex) {
        log.warning("Ignore init-param: class not found, " + clsnm);
      } catch (ClassCastException ex) {
        log.warning("Ignore: " + clsnm + " not implement " + AuExtension.class);
      } catch (Throwable ex) {
        log.warning("Ignore init-param: failed to add an AU extension, " + param, ex);
      }
    }

    if (getAuExtension("/upload") == null) {
      try {
        addAuExtension("/upload", new AuUploader());
      } catch (Throwable ex) {
        final String msg = "Make sure commons-fileupload.jar is installed.";
        log.warningBriefly("Failed to configure fileupload. " + msg, ex);

        // still add /upload to generate exception when fileupload is used
        addAuExtension(
            "/upload",
            new AuExtension() {
              public void init(DHtmlUpdateServlet servlet) {}

              public void destroy() {}

              public void service(
                  HttpServletRequest request, HttpServletResponse response, String pi)
                  throws ServletException, IOException {
                if (Sessions.getCurrent(false) != null)
                  throw new ServletException("Failed to upload. " + msg);
              }
            });
      }
    }

    if (getAuExtension("/view") == null) addAuExtension("/view", new AuDynaMediar());
  }

  public void destroy() {
    for (Iterator it = _aues.values().iterator(); it.hasNext(); ) {
      final AuExtension aue = (AuExtension) it.next();
      try {
        aue.destroy();
      } catch (Throwable ex) {
        log.warningBriefly("Unable to stop " + aue, ex);
      }
    }
  }

  /* Returns whether to compress the output.
   * @since 5.0.0
   */
  public boolean isCompress() {
    return _compress;
  }

  /**
   * Returns the AU extension that is associated the specified prefix.
   *
   * @since 5.0.0
   */
  public static final AuExtension getAuExtension(WebApp wapp, String prefix) {
    DHtmlUpdateServlet upsv = DHtmlUpdateServlet.getUpdateServlet(wapp);
    if (upsv == null) {
      synchronized (DHtmlUpdateServlet.class) {
        upsv = DHtmlUpdateServlet.getUpdateServlet(wapp);
        if (upsv == null) {
          Map aues = (Map) wapp.getAttribute(ATTR_AU_PROCESSORS);
          return aues != null ? (AuExtension) aues.get(prefix) : null;
        }
      }
    }
    return upsv.getAuExtension(prefix);
  }
  /**
   * Adds an AU extension and associates it with the specified prefix, even before {@link
   * DHtmlUpdateServlet} is started.
   *
   * <p>Unlike {@link #addAuExtension(String, AuExtension)}, it can be called even if the update
   * servlet is not loaded yet ({@link #getUpdateServlet} returns null).
   *
   * <p>If there was an AU extension associated with the same name, the the old AU extension will be
   * replaced.
   *
   * @since 5.0.0
   */
  public static final AuExtension addAuExtension(WebApp wapp, String prefix, AuExtension extension)
      throws ServletException {
    DHtmlUpdateServlet upsv = DHtmlUpdateServlet.getUpdateServlet(wapp);
    if (upsv == null) {
      synchronized (DHtmlUpdateServlet.class) {
        upsv = DHtmlUpdateServlet.getUpdateServlet(wapp);
        if (upsv == null) {
          checkAuExtension(prefix, extension);
          Map aues = (Map) wapp.getAttribute(ATTR_AU_PROCESSORS);
          if (aues == null) wapp.setAttribute(ATTR_AU_PROCESSORS, aues = new HashMap(4));
          return (AuExtension) aues.put(prefix, extension);
        }
      }
    }

    return upsv.addAuExtension(prefix, extension);
  }
  /**
   * Adds an AU extension and associates it with the specified prefix.
   *
   * <p>If there was an AU extension associated with the same name, the the old AU extension will be
   * replaced.
   *
   * <p>If you want to add an Au extension, even before DHtmlUpdateServlet is started, use {@link
   * #addAuExtension(WebApp, String, AuExtension)} instead.
   *
   * @param prefix the prefix. It must start with "/", but it cannot be "/" nor "/web" (which are
   *     reserved).
   * @param extension the AU extension (never null).
   * @return the previous AU extension associated with the specified prefix, or null if the prefix
   *     was not associated before.
   * @see #addAuExtension(WebApp,String,AuExtension)
   * @since 5.0.0
   */
  public AuExtension addAuExtension(String prefix, AuExtension extension) throws ServletException {
    checkAuExtension(prefix, extension);

    if (_aues.get(prefix) == extension) // speed up to avoid sync
    return extension; // nothing changed

    extension.init(this);

    // To avoid using sync in doGet(), we make a copy here
    final AuExtension old;
    synchronized (this) {
      final Map ps = new HashMap(_aues);
      old = (AuExtension) ps.put(prefix, extension);
      _aues = ps;
    }
    if (old != null)
      try {
        old.destroy();
      } catch (Throwable ex) {
        log.warningBriefly("Unable to stop " + old, ex);
      }
    return old;
  }

  private static void checkAuExtension(String prefix, AuExtension extension) {
    if (prefix == null || !prefix.startsWith("/") || prefix.length() < 2 || extension == null)
      throw new IllegalArgumentException();
    if (ClassWebResource.PATH_PREFIX.equalsIgnoreCase(prefix))
      throw new IllegalArgumentException(ClassWebResource.PATH_PREFIX + " is reserved");
  }
  /**
   * Returns the AU extension associated with the specified prefix, or null if no AU extension
   * associated.
   *
   * @since 5.0.0
   */
  public AuExtension getAuExtension(String prefix) {
    return (AuExtension) _aues.get(prefix);
  }
  /** Returns the first AU extension matches the specified path, or null if not found. */
  private AuExtension getAuExtensionByPath(String path) {
    for (Iterator it = _aues.entrySet().iterator(); it.hasNext(); ) {
      final Map.Entry me = (Map.Entry) it.next();
      if (path.startsWith((String) me.getKey())) return (AuExtension) me.getValue();
    }
    return null;
  }

  // -- super --//
  protected long getLastModified(HttpServletRequest request) {
    final String pi = Https.getThisPathInfo(request);
    if (pi != null
        && pi.startsWith(ClassWebResource.PATH_PREFIX)
        && pi.indexOf('*') < 0 // language independent
        && !Servlets.isIncluded(request)) {
      // If a resource extension is registered for the extension,
      // we assume the content is dynamic
      final String ext = Servlets.getExtension(pi, false);
      if (ext == null || getClassWebResource().getExtendlet(ext) == null) {
        if (_lastModified == 0) _lastModified = new Date().getTime();
        // Hard to know when it is modified, so cheat it..
        return _lastModified;
      }
    }
    return -1;
  }

  private ClassWebResource getClassWebResource() {
    return WebManager.getWebManager(getServletContext()).getClassWebResource();
  }

  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    final String pi = Https.getThisPathInfo(request);
    //		if (log.finerable()) log.finer("Path info: "+pi);

    final ServletContext ctx = getServletContext();
    final boolean withpi = pi != null && pi.length() != 0;
    if (withpi && pi.startsWith(ClassWebResource.PATH_PREFIX)) {
      // use HttpSession to avoid loading SerializableSession in GAE
      // and don't retrieve session if possible
      final ClassWebResource cwr = getClassWebResource();
      final HttpSession hsess = shallSession(cwr, pi) ? request.getSession(false) : null;
      Object oldsess = null;
      if (hsess == null) {
        oldsess = SessionsCtrl.getRawCurrent();
        SessionsCtrl.setCurrent(new SessionResolverImpl(ctx, request));
        // it might be created later
      }

      WebApp wapp;
      Session sess;
      final Object old =
          hsess != null
              ? (wapp = WebManager.getWebAppIfAny(ctx)) != null
                      && (sess = SessionsCtrl.getSession(wapp, hsess)) != null
                  ? I18Ns.setup(sess, request, response, "UTF-8")
                  : I18Ns.setup(hsess, request, response, "UTF-8")
              : Charsets.setup(null, request, response, "UTF-8");
      try {
        cwr.service(request, response, pi.substring(ClassWebResource.PATH_PREFIX.length()));
      } finally {
        if (hsess != null) I18Ns.cleanup(request, old);
        else {
          Charsets.cleanup(request, old);
          SessionsCtrl.setRawCurrent(oldsess);
        }
      }
      return; // done
    }

    final Session sess = WebManager.getSession(ctx, request, false);
    if (withpi) {
      final AuExtension aue = getAuExtensionByPath(pi);
      if (aue == null) {
        response.sendError(response.SC_NOT_FOUND);
        log.debug("Unknown path info: " + pi);
        return;
      }

      Object oldsess = null;
      if (sess == null) {
        oldsess = SessionsCtrl.getRawCurrent();
        SessionsCtrl.setCurrent(new SessionResolverImpl(ctx, request));
        // it might be created later
      }

      final Object old =
          sess != null
              ? I18Ns.setup(sess, request, response, "UTF-8")
              : Charsets.setup(null, request, response, "UTF-8");
      try {
        aue.service(request, response, pi);
      } finally {
        if (sess != null) I18Ns.cleanup(request, old);
        else {
          Charsets.cleanup(request, old);
          SessionsCtrl.setRawCurrent(oldsess);
        }
      }
      return; // done
    }

    // AU
    if (sess == null) {
      response.setIntHeader("ZK-Error", response.SC_GONE); // denote timeout

      // Bug 1849088: rmDesktop might be sent after invalidate
      // Bug 1859776: need send response to client for redirect or others
      final WebApp wapp = WebManager.getWebAppIfAny(ctx);
      final String dtid = getAuDecoder(wapp).getDesktopId(request);
      if (dtid != null) sessionTimeout(request, response, wapp, dtid);
      return;
    }

    // Feature 3285074 add no-cache for security risk.
    response.setHeader("Pragma", "no-cache");
    response.setHeader("Cache-Control", "no-cache");
    response.setHeader("Cache-Control", "no-store");
    response.setHeader("Expires", "-1");

    final Object old = I18Ns.setup(sess, request, response, "UTF-8");
    try {
      process(sess, request, response);
    } finally {
      I18Ns.cleanup(request, old);
    }
  }

  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    doGet(request, response);
  }

  private static boolean shallSession(ClassWebResource cwr, String pi) {
    return cwr.getExtendlet(Servlets.getExtension(pi, false)) != null
        || (pi != null && pi.indexOf('*') >= 0);
    // Optimize the access of static resources (for GAE)
  }

  // -- ASYNC-UPDATE --//
  /**
   * Process asynchronous update requests from the client.
   *
   * @since 3.0.0
   */
  protected void process(Session sess, HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    final String errClient = request.getHeader("ZK-Error-Report");
    if (errClient != null)
      if (log.debugable())
        log.debug("Error found at client: " + errClient + "\n" + Servlets.getDetail(request));

    // parse desktop ID
    final WebApp wapp = sess.getWebApp();
    final WebAppCtrl wappc = (WebAppCtrl) wapp;
    final AuDecoder audec = getAuDecoder(wapp);
    final String dtid = audec.getDesktopId(request);
    if (dtid == null) {
      // Bug 1929139: incomplete request (IE only)
      if (log.debugable()) {
        final String msg = "Incomplete request\n" + Servlets.getDetail(request);
        log.debug(msg);
      }

      response.sendError(467, "Incomplete request");
      return;
    }

    Desktop desktop = getDesktop(sess, dtid);
    if (desktop == null) {
      final String cmdId = audec.getFirstCommand(request);
      if (!"rmDesktop".equals(cmdId))
        desktop = recoverDesktop(sess, request, response, wappc, dtid);

      if (desktop == null) {
        response.setIntHeader("ZK-Error", response.SC_GONE); // denote timeout
        sessionTimeout(request, response, wapp, dtid);
        return;
      }
    }
    WebManager.setDesktop(request, desktop);
    // reason: a new page might be created (such as include)

    final String sid = request.getHeader("ZK-SID");
    if (sid != null) // Some client might not have ZK-SID
    response.setHeader("ZK-SID", sid);

    // parse commands
    final Configuration config = wapp.getConfiguration();
    final List aureqs;
    boolean keepAlive = false;
    try {
      final boolean timerKeepAlive = config.isTimerKeepAlive();
      aureqs = audec.decode(request, desktop);
      for (Iterator it = aureqs.iterator(); it.hasNext(); ) {
        final String cmdId = ((AuRequest) it.next()).getCommand();
        keepAlive = !(!timerKeepAlive && Events.ON_TIMER.equals(cmdId)) && !"dummy".equals(cmdId);
        // dummy is used for PollingServerPush for piggyback
        if (keepAlive) break; // done
      }
    } catch (Throwable ex) {
      log.warningBriefly(ex);
      responseError(request, response, Exceptions.getMessage(ex));
      return;
    }

    if (aureqs.isEmpty()) {
      final String errmsg = "Illegal request: cmd required";
      log.debug(errmsg);
      responseError(request, response, errmsg);
      return;
    }

    ((SessionCtrl) sess).notifyClientRequest(keepAlive);

    //		if (log.debugable()) log.debug("AU request: "+aureqs);
    final DesktopCtrl desktopCtrl = (DesktopCtrl) desktop;
    final Execution exec = new ExecutionImpl(getServletContext(), request, response, desktop, null);
    if (sid != null) ((ExecutionCtrl) exec).setRequestId(sid);

    final AuWriter out = AuWriters.newInstance();
    out.setCompress(_compress);
    out.open(
        request,
        response,
        desktop.getDevice().isSupported(Device.RESEND)
            ? getProcessTimeout(config.getResendDelay())
            : 0);
    // Note: getResendDelay() might return nonpositive
    try {
      wappc.getUiEngine().execUpdate(exec, aureqs, out);
    } catch (RequestOutOfSequenceException ex) {
      log.warning(ex.getMessage());
      response.setHeader("ZK-SID", sid);
      response.setIntHeader("ZK-Error", AuResponse.SC_OUT_OF_SEQUENCE);
    }
    out.close(request, response);
  }

  /**
   * Returns the desktop of the specified ID, or null if not found. If null is returned, {@link
   * #recoverDesktop} will be invoked.
   *
   * @param sess the session (never null)
   * @param dtid the desktop ID to look for
   * @since 5.0.3
   */
  protected Desktop getDesktop(Session sess, String dtid) {
    return ((WebAppCtrl) sess.getWebApp()).getDesktopCache(sess).getDesktopIfAny(dtid);
  }

  private static int getProcessTimeout(int resendDelay) {
    if (resendDelay > 0) {
      resendDelay = (resendDelay * 3) >> 2;
      if (resendDelay <= 0) resendDelay = 1;
    }
    return resendDelay;
  }
  /** @param wapp the Web application (or null if not available yet) */
  private void sessionTimeout(
      HttpServletRequest request, HttpServletResponse response, WebApp wapp, String dtid)
      throws ServletException, IOException {
    final String sid = request.getHeader("ZK-SID");
    if (sid != null) response.setHeader("ZK-SID", sid);

    final AuWriter out = AuWriters.newInstance().open(request, response, 0);

    if (!getAuDecoder(wapp).isIgnorable(request, wapp)) {
      final String deviceType = getDeviceType(request);
      URIInfo ui =
          wapp != null ? (URIInfo) wapp.getConfiguration().getTimeoutURI(deviceType) : null;
      String uri = ui != null ? ui.uri : null;
      out.write(new AuConfirmClose(null)); // Bug: B50-3147382
      final AuResponse resp;
      if (uri != null) {
        if (uri.length() != 0) uri = Encodes.encodeURL(getServletContext(), request, response, uri);
        resp = new AuSendRedirect(uri, null);
      } else {
        String msg = wapp.getConfiguration().getTimeoutMessage(deviceType);
        if (msg != null && msg.startsWith("label:")) {
          final String key;
          msg = Labels.getLabel(key = msg.substring(6), new Object[] {dtid});
          if (msg == null) log.warning("Label not found, " + key);
        }
        if (msg == null) msg = Messages.get(MZk.UPDATE_OBSOLETE_PAGE, dtid);
        resp = new AuObsolete(dtid, msg);
      }
      out.write(resp);
    }

    out.close(request, response);
  }

  private static String getDeviceType(HttpServletRequest request) {
    final String agt = request.getHeader("user-agent");
    if (agt != null && agt.length() > 0) {
      try {
        return Devices.getDeviceByClient(agt).getType();
      } catch (Throwable ex) {
        log.warning("Unknown device for " + agt);
      }
    }
    return "ajax";
  }

  /**
   * Recovers the desktop if possible. It is called if {@link #getDesktop} returns null.
   *
   * <p>The default implementation will look for any failover manager ({@link FailoverManager}) is
   * registered, and forward the invocation to it if found.
   *
   * @return the recovered desktop, or null if failed to recover
   * @since 5.0.3
   */
  protected Desktop recoverDesktop(
      Session sess,
      HttpServletRequest request,
      HttpServletResponse response,
      WebAppCtrl wappc,
      String dtid) {
    final FailoverManager failover = wappc.getFailoverManager();
    if (failover != null) {
      Desktop desktop = null;
      final ServletContext ctx = getServletContext();
      try {
        if (failover.isRecoverable(sess, dtid)) {
          desktop = WebManager.getWebManager(ctx).getDesktop(sess, request, response, null, true);
          if (desktop == null) // forward or redirect
          throw new IllegalStateException("sendRediect or forward not allowed in recovering");

          wappc
              .getUiEngine()
              .execRecover(new ExecutionImpl(ctx, request, response, desktop, null), failover);
          return desktop; // success
        }
      } catch (Throwable ex) {
        log.error("Unable to recover " + dtid, ex);
        if (desktop != null) ((DesktopCtrl) desktop).recoverDidFail(ex);
      }
    }
    return null;
  }

  /** Generates a response for an error message. */
  private static void responseError(
      HttpServletRequest request, HttpServletResponse response, String errmsg) throws IOException {
    // Don't use sendError because Browser cannot handle UTF-8
    AuWriter out = AuWriters.newInstance().open(request, response, 0);
    out.write(new AuAlert(errmsg));
    out.close(request, response);
  }

  private static final AuDecoder getAuDecoder(WebApp wapp) {
    AuDecoder audec = wapp != null ? ((WebAppCtrl) wapp).getAuDecoder() : null;
    return audec != null ? audec : _audec;
  }

  private static final AuDecoder _audec =
      new AuDecoder() {
        public String getDesktopId(Object request) {
          return ((HttpServletRequest) request).getParameter("dtid");
        }

        public String getFirstCommand(Object request) {
          return ((HttpServletRequest) request).getParameter("cmd.0");
        }

        public List decode(Object request, Desktop desktop) {
          final List aureqs = new LinkedList();
          final HttpServletRequest hreq = (HttpServletRequest) request;
          for (int j = 0; ; ++j) {
            final String cmdId = hreq.getParameter("cmd_" + j);
            if (cmdId == null) break;

            final String uuid = hreq.getParameter("uuid_" + j);
            final String data = hreq.getParameter("data_" + j);
            final Map decdata = (Map) JSONValue.parse(data);
            aureqs.add(
                uuid == null || uuid.length() == 0
                    ? new AuRequest(desktop, cmdId, decdata)
                    : new AuRequest(desktop, uuid, cmdId, decdata));
          }
          return aureqs;
        }

        public boolean isIgnorable(Object request, WebApp wapp) {
          final HttpServletRequest hreq = (HttpServletRequest) request;
          for (int j = 0; ; ++j) {
            if (hreq.getParameter("cmd_" + j) == null) break;

            final String opt = hreq.getParameter("opt_" + j);
            if (opt == null || opt.indexOf("i") < 0) return false; // not ignorable
          }
          return true;
        }
      };
}
Esempio n. 6
0
/**
 * To help invoke a method with {@link BindingParam} etc.. features.
 *
 * @author dennis
 * @since 6.0.0
 */
public class ParamCall {

  private static final Log _log = Log.lookup(ParamCall.class);

  private Map<Class<? extends Annotation>, ParamResolver<Annotation>> _paramResolvers;
  private List<Type> _types; // to map class type directly, regardless the annotation
  private boolean
      _mappingType; // to enable the map class type without annotation, it is for compatible to rc2,
  // only support BindeContext and Binder
  private ContextObjects _contextObjects;

  private static final String COOKIE_CACHE = "$PARAM_COOKIES$";

  private Component _root = null;
  private Component _component = null;
  private Execution _execution = null;
  private Binder _binder = null;
  private BindContext _bindContext = null;

  public ParamCall() {
    this(true);
  }

  public ParamCall(boolean mappingType) {
    _paramResolvers = new HashMap<Class<? extends Annotation>, ParamResolver<Annotation>>();
    _contextObjects = new ContextObjects();
    _types = new ArrayList<Type>();
    _mappingType = mappingType;
    _paramResolvers.put(
        ContextParam.class,
        new ParamResolver<Annotation>() {

          public Object resolveParameter(Annotation anno, Class<?> returnType) {
            Object val = _contextObjects.get(((ContextParam) anno).value());
            return val == null ? null : Classes.coerce(returnType, val);
          }
        });
  }

  public void setBindContext(BindContext ctx) {
    _bindContext = ctx;
    _types.add(new Type(ctx.getClass(), _bindContext));
  }

  public void setBinder(Binder binder) {
    _binder = binder;
    _types.add(new Type(binder.getClass(), _binder));
    _root = binder.getView();
  }

  public void setBindingArgs(final Map<String, Object> bindingArgs) {
    _paramResolvers.put(
        BindingParam.class,
        new ParamResolver<Annotation>() {

          public Object resolveParameter(Annotation anno, Class<?> returnType) {
            Object val = bindingArgs.get(((BindingParam) anno).value());
            return val == null ? null : Classes.coerce(returnType, val);
          }
        });
  }

  public void call(Object base, Method method) {
    Class<?>[] paramTypes = method.getParameterTypes();
    java.lang.annotation.Annotation[][] parmAnnos = method.getParameterAnnotations();
    Object[] params = new Object[paramTypes.length];

    try {
      for (int i = 0; i < paramTypes.length; i++) {
        params[i] = resolveParameter(parmAnnos[i], paramTypes[i]);
      }

      method.invoke(base, params);
    } catch (InvocationTargetException invokEx) {
      // Ian YT Tsai (2012.06.20), while InvocationTargetException,
      // using original exception is much meaningful.
      Throwable c = invokEx.getCause();
      if (c == null) c = invokEx;
      _log.error(c);
      throw UiException.Aide.wrap(c);
    } catch (Exception e) {
      _log.error(e);
      throw UiException.Aide.wrap(e);
    }
  }

  private Object resolveParameter(java.lang.annotation.Annotation[] parmAnnos, Class<?> paramType) {
    Object val = null;
    boolean hitResolver = false;
    Default defAnno = null;
    for (Annotation anno : parmAnnos) {
      Class<?> annotype = anno.annotationType();

      if (defAnno == null && annotype.equals(Default.class)) {
        defAnno = (Default) anno;
        continue;
      }
      ParamResolver<Annotation> resolver = _paramResolvers.get(annotype);
      if (resolver == null) continue;
      hitResolver = true;
      val = resolver.resolveParameter(anno, paramType);
      if (val != null) {
        break;
      }
      // don't break until get a value
    }
    if (val == null && defAnno != null) {
      val = Classes.coerce(paramType, defAnno.value());
    }

    // to compatible to rc2, do we have to?
    if (_mappingType && val == null && !hitResolver && _types != null) {
      for (Type type : _types) {
        if (type != null && paramType.isAssignableFrom(type.clz)) {
          val = type.value;
          break;
        }
      }
    }
    return val;
  }

  // utility to hold implicit class and runtime value
  private static class Type {
    final Class<?> clz;
    final Object value;

    public Type(Class<?> clz, Object value) {
      this.clz = clz;
      this.value = value;
    }
  }

  private interface ParamResolver<T> {
    public Object resolveParameter(T anno, Class<?> returnType);
  }

  public void setComponent(Component comp) {
    _component = comp;
    // scope param
    _paramResolvers.put(
        ScopeParam.class,
        new ParamResolver<Annotation>() {

          public Object resolveParameter(Annotation anno, Class<?> returnType) {
            final String name = ((ScopeParam) anno).value();
            final Scope[] ss = ((ScopeParam) anno).scopes();

            Object val = null;

            for (Scope s : ss) {
              switch (s) {
                case AUTO:
                  if (ss.length == 1) {
                    val = _component.getAttribute(name, true);
                  } else {
                    throw new UiException(
                        "don't use " + s + " with other scopes " + Arrays.toString(ss));
                  }
              }
            }
            if (val == null) {
              for (Scope scope : ss) {
                final String scopeName = scope.getName();
                Object scopeObj = Components.getImplicit(_component, scopeName);
                if (scopeObj instanceof Map) {
                  val = ((Map<?, ?>) scopeObj).get(name);
                  if (val != null) break;
                } else if (scopeObj != null) {
                  _log.error("the scope of " + scopeName + " is not a Map, is " + scopeObj);
                }
              }
            }

            // zk-1469,
            if (val instanceof ReferenceBinding) {
              val = resolveReferenceBinding(name, (ReferenceBinding) val, returnType);
            }
            return val == null ? null : Classes.coerce(returnType, val);
          }
        });

    // component
    _paramResolvers.put(
        SelectorParam.class,
        new ParamResolver<Annotation>() {

          public Object resolveParameter(Annotation anno, Class<?> returnType) {
            final String selector = ((SelectorParam) anno).value();
            final List<Component> result = Selectors.find(_root, selector);
            Object val;
            if (!Collection.class.isAssignableFrom(returnType)) {
              val = result.size() > 0 ? Classes.coerce(returnType, result.get(0)) : null;
            } else {
              val = Classes.coerce(returnType, result);
            }
            return val;
          }
        });
  }

  private Object resolveReferenceBinding(
      String name, ReferenceBinding rbinding, Class<?> returnType) {
    BindEvaluatorX evalx = rbinding.getBinder().getEvaluatorX();
    // resolve by name or by rbinding.propertyString directly?
    Object val = BindEvaluatorXUtil.eval(evalx, rbinding.getComponent(), name, returnType, null);
    // following is quick but not safe because of the null arg
    //		val = ((ReferenceBinding)val).getValue(null);

    return val;
  }

  public void setExecution(Execution exec) {
    _execution = exec;
    // http param
    _paramResolvers.put(
        QueryParam.class,
        new ParamResolver<Annotation>() {

          public Object resolveParameter(Annotation anno, Class<?> returnType) {
            Object val = _execution.getParameter(((QueryParam) anno).value());
            return val == null ? null : Classes.coerce(returnType, val);
          }
        });
    _paramResolvers.put(
        HeaderParam.class,
        new ParamResolver<Annotation>() {

          public Object resolveParameter(Annotation anno, Class<?> returnType) {
            Object val = _execution.getHeader(((HeaderParam) anno).value());
            return val == null ? null : Classes.coerce(returnType, val);
          }
        });
    _paramResolvers.put(
        CookieParam.class,
        new ParamResolver<Annotation>() {

          @SuppressWarnings("unchecked")
          public Object resolveParameter(Annotation anno, Class<?> returnType) {
            Map<String, Object> m = (Map<String, Object>) _execution.getAttribute(COOKIE_CACHE);
            if (m == null) {
              final Object req = _execution.getNativeRequest();
              m = new HashMap<String, Object>();
              _execution.setAttribute(COOKIE_CACHE, m);

              if (req instanceof HttpServletRequest) {
                final Cookie[] cks = ((HttpServletRequest) req).getCookies();
                if (cks != null) {
                  for (Cookie ck : cks) {
                    m.put(ck.getName().toLowerCase(), ck.getValue());
                  }
                }
              } else /* if(req instanceof PortletRequest)*/ {
                // no cookie in protlet 1.0
              }
            }
            Object val = m == null ? null : m.get(((CookieParam) anno).value().toLowerCase());
            return val == null ? null : Classes.coerce(returnType, val);
          }
        });

    // execution
    _paramResolvers.put(
        ExecutionParam.class,
        new ParamResolver<Annotation>() {

          public Object resolveParameter(Annotation anno, Class<?> returnType) {
            Object val = _execution.getAttribute(((ExecutionParam) anno).value());
            return val == null ? null : Classes.coerce(returnType, val);
          }
        });

    _paramResolvers.put(
        ExecutionArgParam.class,
        new ParamResolver<Annotation>() {

          public Object resolveParameter(Annotation anno, Class<?> returnType) {
            Object val = _execution.getArg().get(((ExecutionArgParam) anno).value());
            return val == null ? null : Classes.coerce(returnType, val);
          }
        });
  }

  class ContextObjects {
    public Object get(ContextType type) {
      switch (type) {
          // bind contexts
        case BIND_CONTEXT:
          return _bindContext;
        case BINDER:
          return _binder;
        case COMMAND_NAME:
          return _bindContext == null ? null : _bindContext.getCommandName();
        case TRIGGER_EVENT:
          return _bindContext == null ? null : _bindContext.getTriggerEvent();
          // zk execution contexts
        case EXECUTION:
          return _execution;
        case COMPONENT:
          return _component;
        case SPACE_OWNER:
          return _component == null ? null : _component.getSpaceOwner();
        case VIEW:
          return _binder == null ? null : _binder.getView();
        case PAGE:
          return _component == null ? null : _component.getPage();
        case DESKTOP:
          return _component == null ? null : _component.getDesktop();
        case SESSION:
          return _component == null ? null : Components.getImplicit(_component, "session");
        case APPLICATION:
          return _component == null ? null : Components.getImplicit(_component, "application");
      }
      return null;
    }
  }
}
Esempio n. 7
0
/**
 * Functions to manipulate strings in EL.
 *
 * @author tomyeh
 */
public class StringFns {
  private static Log log = Log.lookup(StringFns.class);

  /** Catenates two strings. Note: null is considered as empty. */
  public static String cat(String s1, String s2) {
    if (s1 == null) return s2 != null ? s2 : "";
    return s2 != null ? s1 + s2 : s1;
  }
  /** Catenates three strings. Note: null is considered as empty. */
  public static String cat3(String s1, String s2, String s3) {
    return cat(cat(s1, s2), s3);
  }
  /** Catenates four strings. Note: null is considered as empty. */
  public static String cat4(String s1, String s2, String s3, String s4) {
    return cat(cat(cat(s1, s2), s3), s4);
  }
  /** Catenates four strings. Note: null is considered as empty. */
  public static String cat5(String s1, String s2, String s3, String s4, String s5) {
    return cat(cat(cat(cat(s1, s2), s3), s4), s5);
  }

  /**
   * Converts all of the characters in this String to upper case using the rules of the current
   * Locale.
   *
   * @see Locales#getCurrent
   * @since 5.0.7
   */
  public static String toLowerCase(String s) {
    return s != null ? s.toLowerCase(Locales.getCurrent()) : null;
  }
  /**
   * Converts all of the characters in this String to upper case using the rules of the current
   * Locale.
   *
   * @see Locales#getCurrent
   * @since 5.0.7
   */
  public static String toUpperCase(String s) {
    return s != null ? s.toUpperCase(Locales.getCurrent()) : null;
  }
  /**
   * Returns a copy of the string, with leading and trailing whitespace omitted.
   *
   * @since 5.0.7
   */
  public static String trim(String s) {
    return s != null ? s.trim() : null;
  }
  /**
   * Splits a string.
   *
   * @since 5.0.7
   */
  public static String[] split(String s, String separator) {
    return s != null ? s.split(separator) : null;
  }
  /** Joins an array of string. since 5.0.7 */
  public static String join(Object[] ss, String separator) {
    if (ss == null) return null;

    final StringBuffer sb = new StringBuffer();
    for (int j = 0; j < ss.length; ++j) {
      if (j != 0) sb.append(separator);
      sb.append(ss[j]);
    }
    return ss.toString();
  }
  /**
   * Tests if this string starts with the specified prefix.
   *
   * @since 5.0.7
   */
  public static boolean startsWith(String s1, String s2) {
    return s1 != null && s2 != null && s1.startsWith(s2);
  }
  /**
   * Tests if this string ends with the specified suffix.
   *
   * @since 5.0.7
   */
  public static boolean endsWith(String s1, String s2) {
    return s1 != null && s2 != null && s1.endsWith(s2);
  }
  /**
   * Returns a new string that is a substring of this string.
   *
   * @since 5.0.7
   */
  public static String substring(String s, int from, int to) {
    return s != null ? s.substring(from, to) : null;
  }
  /** Replaces all occurrances of 'from' in 'src' with 'to' */
  public static String replace(String src, String from, String to) {
    if (Objects.equals(from, to)) return src;

    final StringBuffer sb = new StringBuffer(src);
    if ("\n".equals(from) || "\r\n".equals(from)) {
      replace0(sb, "\r\n", to);
      replace0(sb, "\n", to);
    } else {
      replace0(sb, from, to);
    }
    return sb.toString();
  }

  private static void replace0(StringBuffer sb, String from, String to) {
    final int len = from.length();
    for (int j = 0; (j = sb.indexOf(from, j)) >= 0; ) {
      sb.replace(j, j += len, to);
    }
  }

  /**
   * Eliminates single and double quotations to avoid JavaScript injection. It eliminates all
   * quotations. In other words, the specified string shall NOT contain any quotations.
   *
   * <p>It is used to avoid JavaScript injection. For exmple, in DSP or JSP pages, the following
   * codes is better to escape with this method. <code><input value="${c:eatQuot(param.some)}"/>
   * </code>
   *
   * @since 3.5.2
   */
  public static String eatQuot(String s) {
    final int len = s != null ? s.length() : 0;
    StringBuffer sb = null;
    for (int j = 0; j < len; ++j) {
      final char cc = s.charAt(j);
      if (cc == '\'' || cc == '"') {
        if (sb == null) {
          log.warning("JavaScript Injection? Unexpected string detected: " + s);
          sb = new StringBuffer(len);
          if (j > 0) sb.append(s.substring(0, j));
        }
      } else if (sb != null) sb.append(cc);
    }
    return sb != null ? sb.toString() : s;
  }
}
Esempio n. 8
0
/**
 * A window.
 *
 * <p>Unlike other elements, each {@link Window} is an independent ID space (by implementing {@link
 * org.zkoss.zk.ui.IdSpace}). It means a window and all its descendants forms a ID space and the ID
 * of each of them is unique in this space. You could retrieve any of them in this space by calling
 * {@link #getFellow}.
 *
 * <p>If a window X is a descendant of another window Y, X's descendants are not visible in Y's
 * space. To retrieve a descendant, say Z, of X, you have to invoke Y.getFellow('X').getFellow('Z').
 *
 * <p>Events:<br>
 * onMove, onOpen, onMaximize, onMinimize, and onClose.<br>
 * Note: to have better performance, onOpen is sent only if a non-deferrable event listener is
 * registered (see {@link org.zkoss.zk.ui.event.Deferrable}).
 *
 * <p><code>onMaximize</code> and <code>onMinimize</code> are supported. (since 3.5.0)
 *
 * <p><code>onClose</code> is sent when the close button is pressed (if {@link #isClosable} is
 * true). The window has to detach or hide the window. By default, {@link #onClose} detaches the
 * window. To prevent it from detached, you have to call {@link
 * org.zkoss.zk.ui.event.Event#stopPropagation} to prevent {@link #onClose} is called.
 *
 * <p>On the other hand, <code>onOpen</code> is sent when a popup window (i.e., {@link #getMode} is
 * popup) is closed due to user's activity (such as press ESC). This event is only a notification.
 * In other words, the popup is hidden before the event is sent to the server. The application
 * cannot prevent the window from being hidden.
 *
 * <p>Default {@link #getZclass}: z-window-{@link #getMode()}.(since 3.5.0)
 *
 * @author tomyeh
 */
public class Window extends XulElement implements org.zkoss.zul.api.Window, Framable, IdSpace {
  private static final Log log = Log.lookup(Window.class);
  private static final long serialVersionUID = 20100721L;

  private transient Caption _caption;

  private String _border = "none";
  private String _title = "";
  /** One of MODAL, _MODAL_, EMBEDDED, OVERLAPPED, HIGHLIGHTED, POPUP. */
  private int _mode = EMBEDDED;
  /** Used for doModal. */
  private Mutex _mutex = new Mutex();
  /** The style used for the content block. */
  private String _cntStyle;
  /** The style class used for the content block. */
  private String _cntSclass;
  /** How to position the window. */
  private String _pos;
  /** Whether to show a close button. */
  private boolean _closable;
  /** Whether the window is sizable. */
  private boolean _sizable;
  /** Whether to show the shadow. */
  private boolean _shadow = true;

  private boolean _maximizable, _minimizable, _maximized, _minimized;
  private int _minheight = 100, _minwidth = 200;

  /** Embeds the window as normal component. */
  public static final int EMBEDDED = 0;
  /**
   * Makes the window as a modal dialog. once {@link #doModal} is called, the execution of the event
   * processing thread is suspended until one of the following occurs.
   *
   * <ol>
   *   <li>{@link #setMode} is called with a mode other than MODAL.
   *   <li>Either {@link #doOverlapped}, {@link #doPopup}, {@link #doEmbedded}, or {@link
   *       #doHighlighted} is called.
   *   <li>{@link #setVisible} is called with false.
   *   <li>The window is detached from the window.
   * </ol>
   *
   * <p>Note: In the last two cases, the mode becomes {@link #OVERLAPPED}. In other words, one might
   * say a modal window is a special overlapped window.
   *
   * @see #HIGHLIGHTED
   */
  public static final int MODAL = 1;
  // Represent a modal when the event thread is disabled (internal)
  private static final int _MODAL_ = -100;
  /** Makes the window as overlapped other components. */
  public static final int OVERLAPPED = 2;
  /**
   * Makes the window as popup. It is similar to {@link #OVERLAPPED}, except it is auto hidden when
   * user clicks outside of the window.
   */
  public static final int POPUP = 3;
  /**
   * Makes the window as highlighted. Its visual effect is the same as {@link #MODAL}. However, from
   * the server side's viewpoint, it is similar to {@link #OVERLAPPED}. The execution won't be
   * suspended when {@link #doHighlighted} is called.
   *
   * @see #MODAL
   * @see #OVERLAPPED
   */
  public static final int HIGHLIGHTED = 4;

  static {
    addClientEvent(Window.class, Events.ON_CLOSE, 0);
    addClientEvent(Window.class, Events.ON_MOVE, CE_DUPLICATE_IGNORE | CE_IMPORTANT);
    addClientEvent(Window.class, Events.ON_SIZE, CE_DUPLICATE_IGNORE | CE_IMPORTANT);
    addClientEvent(Window.class, Events.ON_OPEN, CE_IMPORTANT);
    addClientEvent(Window.class, Events.ON_Z_INDEX, CE_DUPLICATE_IGNORE | CE_IMPORTANT);
    addClientEvent(Window.class, Events.ON_MAXIMIZE, CE_DUPLICATE_IGNORE | CE_IMPORTANT);
    addClientEvent(Window.class, Events.ON_MINIMIZE, CE_DUPLICATE_IGNORE | CE_IMPORTANT);
  }

  public Window() {
    setAttribute(
        "z$is",
        Boolean
            .TRUE); // optional but optimized to mean no need to generate z$is since client handles
                    // it
  }
  /**
   * @param title the window title (see {@link #setTitle}).
   * @param border the border (see {@link #setBorder}).
   * @param closable whether it is closable (see {@link #setClosable}).
   */
  public Window(String title, String border, boolean closable) {
    this();
    setTitle(title);
    setBorder(border);
    setClosable(closable);
  }

  /**
   * Returns whether the window is maximized.
   *
   * @since 3.5.0
   */
  public boolean isMaximized() {
    return _maximized;
  }
  /**
   * Sets whether the window is maximized, and then the size of the window will depend on it to show
   * a appropriate size. In other words, if true, the size of the window will count on the size of
   * its offset parent node whose position is absolute (by not {@link #inEmbedded()}) or its parent
   * node. Otherwise, its size will be original size. Note that the maximized effect will run at
   * client's sizing phase not initial phase.
   *
   * <p>Default: false.
   *
   * @exception UiException if {@link #isMaximizable} is false.
   * @since 3.5.0
   */
  public void setMaximized(boolean maximized) {
    if (_maximized != maximized) {
      if (!_maximizable) throw new UiException("Not maximizable, " + this);

      _maximized = maximized;
      if (_maximized) {
        _minimized = false;
        setVisible0(true); // avoid dead loop
      }
      smartUpdate("maximized", _maximized);
    }
  }
  /**
   * Returns whether to display the maximizing button and allow the user to maximize the window.
   *
   * <p>Default: false.
   *
   * @since 3.5.0
   */
  public boolean isMaximizable() {
    return _maximizable;
  }
  /**
   * Sets whether to display the maximizing button and allow the user to maximize the window, when a
   * window is maximized, the button will automatically change to a restore button with the
   * appropriate behavior already built-in that will restore the window to its previous size.
   *
   * <p>Default: false.
   *
   * <p>Note: the maximize button won't be displayed if no title or caption at all.
   *
   * @since 3.5.0
   */
  public void setMaximizable(boolean maximizable) {
    if (_maximizable != maximizable) {
      _maximizable = maximizable;
      smartUpdate("maximizable", _maximizable);
    }
  }

  /**
   * Returns whether the window is minimized.
   *
   * <p>Default: false.
   *
   * @since 3.5.0
   */
  public boolean isMinimized() {
    return _minimized;
  }
  /**
   * Sets whether the window is minimized.
   *
   * <p>Default: false.
   *
   * @exception UiException if {@link #isMinimizable} is false.
   * @since 3.5.0
   */
  public void setMinimized(boolean minimized) {
    if (_minimized != minimized) {
      if (!_minimizable) throw new UiException("not minimizable, " + this);

      _minimized = minimized;
      if (_minimized) {
        _maximized = false;
        setVisible0(false); // avoid dead loop
      } else setVisible0(true);
      smartUpdate("minimized", _minimized);
    }
  }
  /**
   * Returns whether to display the minimizing button and allow the user to minimize the window.
   *
   * <p>Default: false.
   *
   * @since 3.5.0
   */
  public boolean isMinimizable() {
    return _minimizable;
  }
  /**
   * Sets whether to display the minimizing button and allow the user to minimize the window. Note
   * that this button provides no implementation -- the behavior of minimizing a window is
   * implementation-specific, so the MinimizeEvent event must be handled and a custom minimize
   * behavior implemented for this option to be useful.
   *
   * <p>Default: false.
   *
   * <p>Note: the maximize button won't be displayed if no title or caption at all.
   *
   * @see MinimizeEvent
   * @since 3.5.0
   */
  public void setMinimizable(boolean minimizable) {
    if (_minimizable != minimizable) {
      _minimizable = minimizable;
      smartUpdate("minimizable", _minimizable);
    }
  }
  /**
   * Sets the minimum height in pixels allowed for this window. If negative, 100 is assumed.
   *
   * <p>Default: 100.
   *
   * <p>Note: Only applies when {@link #isSizable()} = true.
   *
   * @since 3.5.0
   */
  public void setMinheight(int minheight) {
    if (minheight < 0) minheight = 100;
    if (_minheight != minheight) {
      _minheight = minheight;
      smartUpdate("minheight", _minheight);
    }
  }
  /**
   * Returns the minimum height.
   *
   * <p>Default: 100.
   *
   * @since 3.5.0
   */
  public int getMinheight() {
    return _minheight;
  }
  /**
   * Sets the minimum width in pixels allowed for this window. If negative, 200 is assumed.
   *
   * <p>Default: 200.
   *
   * <p>Note: Only applies when {@link #isSizable()} = true.
   *
   * @since 3.5.0
   */
  public void setMinwidth(int minwidth) {
    if (minwidth < 0) minwidth = 200;
    if (_minwidth != minwidth) {
      _minwidth = minwidth;
      smartUpdate("minwidth", _minwidth);
    }
  }
  /**
   * Returns the minimum width.
   *
   * <p>Default: 200.
   *
   * @since 3.5.0
   */
  public int getMinwidth() {
    return _minwidth;
  }
  /**
   * @deprecated As release of 5.0.0, replaced with {@link
   *     org.zkoss.zk.ui.HtmlBasedComponent#setAction}.
   */
  public static void setDefaultActionOnShow(String onshow) {}

  /**
   * @deprecated As release of 5.0.0, replaced with {@link
   *     org.zkoss.zk.ui.HtmlBasedComponent#setAction}.
   */
  public static String getDefaultActionOnShow() {
    return null;
  }

  /** Returns the caption of this window. */
  public Caption getCaption() {
    return _caption;
  }
  /**
   * Returns the caption of this window.
   *
   * @since 3.5.2
   */
  public org.zkoss.zul.api.Caption getCaptionApi() {
    return getCaption();
  }

  /**
   * Returns the border.
   *
   * <p>Default: "none".
   */
  public String getBorder() {
    return _border;
  }
  /**
   * Sets the border (either none or normal).
   *
   * @param border the border. If null, "0" or "false", "none" is assumed. If "true", "normal" is
   *     assumed (since 5.0.8).
   */
  public void setBorder(String border) {
    if (border == null || "0".equals(border) || "false".equals(border)) border = "none";
    else if ("true".equals(border)) border = "normal";
    if (!Objects.equals(_border, border)) {
      _border = border;
      smartUpdate("border", border);
    }
  }
  /**
   * Enables or disables the border.
   *
   * @param border whether to have a border. If true is specified, it is the same as <code>
   *     setBorder("normal")</code>.
   * @since 5.0.8
   */
  public void setBorder(boolean border) {
    setBorder(border ? "normal" : "none");
  }

  /**
   * Returns the title. Besides this attribute, you could use {@link Caption} to define a more
   * sophiscated caption (aka., title).
   *
   * <p>If a window has a caption whose label ({@link Caption#getLabel}) is not empty, then this
   * attribute is ignored.
   *
   * <p>Default: empty.
   */
  public String getTitle() {
    return _title;
  }
  /** Sets the title. */
  public void setTitle(String title) {
    if (title == null) title = "";
    if (!Objects.equals(_title, title)) {
      _title = title;
      smartUpdate("title", title);
    }
  }

  /**
   * Returns the current mode. One of "modal", "embedded", "overlapped", "popup", and "highlighted".
   */
  public String getMode() {
    return modeToString(_mode);
  }

  private static String modeToString(int mode) {
    switch (mode) {
      case MODAL:
      case _MODAL_:
        return "modal";
      case POPUP:
        return "popup";
      case OVERLAPPED:
        return "overlapped";
      case HIGHLIGHTED:
        return "highlighted";
      default:
        return "embedded";
    }
  }
  /**
   * Sets the mode to overlapped, popup, modal, embedded or highlighted.
   *
   * <p>Notice: {@link Events#ON_MODAL} is posted if you specify "modal" to this method. Unlike
   * {@link #doModal}, {@link Events#ON_MODAL} is posted, so the window will become modal later
   * (since 3.0.4). In other words, setMode("modal") never suspends the execution of the current
   * thread. On the other hand, {@link #doModal} will suspends the execution if executed in an event
   * listener, or throws an exception if <em>not</em> executed in an event listener.
   *
   * <p>Refer to <a
   * href="http://books.zkoss.org/wiki/ZK_Component_Reference/Containers/Window">Overlapped, Popup,
   * Modal, Highlighted and Embedded</a> for more information.
   *
   * <p>If the event processing thread is disabled (it is the default), InterruptedException won't
   * be thrown.
   *
   * @param name the mode which could be one of "embedded", "overlapped", "popup", "modal",
   *     "highlighted". Note: it cannot be "modal". Use {@link #doModal} instead.
   * @exception InterruptedException thrown if "modal" is specified the event thread is enabled
   *     (disabled by default), and one of the following conditions occurs: 1) the desktop or the
   *     Web application is being destroyed, or 2) {@link
   *     org.zkoss.zk.ui.sys.DesktopCtrl#ceaseSuspendedThread}. To tell the difference, check the
   *     getMessage method of InterruptedException.
   */
  public void setMode(String name) throws InterruptedException {
    if ("popup".equals(name)) doPopup();
    else if ("overlapped".equals(name)) doOverlapped();
    else if ("embedded".equals(name)) doEmbedded();
    else if ("modal".equals(name)) {
      if (isEventThreadEnabled(false)) Events.postEvent(Events.ON_MODAL, this, null);
      else doModal();
    } else if ("highlighted".equals(name)) doHighlighted();
    else throw new WrongValueException("Unknown mode: " + name);
  }
  /**
   * Sets the mode to overlapped, popup, modal, embedded or highlighted.
   *
   * @see #setMode(String)
   */
  public void setMode(int mode) throws InterruptedException {
    switch (mode) {
      case POPUP:
        doPopup();
        break;
      case OVERLAPPED:
        doOverlapped();
        break;
      case EMBEDDED:
        doEmbedded();
        break;
      case MODAL:
        if (isEventThreadEnabled(false)) Events.postEvent(Events.ON_MODAL, this, null);
        else doModal();
        break;
      case HIGHLIGHTED:
        doHighlighted();
        break;
      default:
        throw new WrongValueException("Unknown mode: " + mode);
    }
  }

  /** Returns whether this is a modal dialog. */
  public boolean inModal() {
    return _mode == MODAL || _mode == _MODAL_;
  }
  /**
   * Returns whether this is embedded with other components (Default).
   *
   * @see #doEmbedded
   */
  public boolean inEmbedded() {
    return _mode == EMBEDDED;
  }
  /** Returns whether this is a overlapped window. */
  public boolean inOverlapped() {
    return _mode == OVERLAPPED;
  }
  /** Returns whether this is a popup window. */
  public boolean inPopup() {
    return _mode == POPUP;
  }
  /** Returns whether this is a highlighted window. */
  public boolean inHighlighted() {
    return _mode == HIGHLIGHTED;
  }

  /**
   * Makes this window as a modal dialog. It will automatically center the window (ignoring {@link
   * #getLeft} and {@link #getTop}).
   *
   * <p>Notice: though both setMode("modal") and doModal() both causes the window to become modal,
   * they are a bit different. doModal causes the event listener to suspend immediately, while
   * setMode("modal") posts an event ({@link Events#ON_MODAL}). That is, {@link #setMode} won't
   * suspend the execution immediately, but {@link #doModal} will. {@link #doModal} can be called
   * only in an event listener, while {@link #setMode} can be called anytime.
   *
   * @exception SuspendNotAllowedException if 1) not in an event listener;<br>
   *     2) the event thread is disabled.<br>
   *     3) there are too many suspended processing thread than the deployer allows. By default,
   *     there is no limit of # of suspended threads.
   * @exception InterruptedException thrown if the desktop or the Web application is being
   *     destroyed, or {@link org.zkoss.zk.ui.sys.DesktopCtrl#ceaseSuspendedThread}. To tell the
   *     difference, check the getMessage method of InterruptedException.
   * @since 3.0.4
   */
  public void doModal() throws InterruptedException, SuspendNotAllowedException {
    if (!isEventThreadEnabled(true)) {
      checkOverlappable(_MODAL_);
      setNonModalMode(_MODAL_);
      return;
    }

    checkOverlappable(MODAL);

    if (_mode != MODAL) {
      if (!Events.inEventListener())
        throw new SuspendNotAllowedException("doModal must be called in an event listener");

      int oldmode = _mode;
      boolean oldvisi = isVisible();

      setVisible(true); // if MODAL, it must be visible; vice versa

      try {
        enterModal();
      } catch (SuspendNotAllowedException ex) {
        handleFailedModal(oldmode, oldvisi);
        throw ex;
      }
    }
  }

  private void handleFailedModal(int oldmode, boolean oldvisi) {
    try {
      if (Executions.getCurrent().getAttribute("javax.servlet.error.exception") != null) {
        // handle it specially if it is used for dispalying err
        setMode(HIGHLIGHTED);
      } else {
        setMode(oldmode); // restore
        setVisible(oldvisi);
      }
    } catch (Throwable ex) {
      log.realCauseBriefly("Causing another error", ex);
    }
  }

  /** Makes this window as overlapped with other components. */
  public void doOverlapped() {
    checkOverlappable(OVERLAPPED);
    setNonModalMode(OVERLAPPED);
  }
  /**
   * Makes this window as popup, which is overlapped with other component and auto-hiden when user
   * clicks outside of the window.
   */
  public void doPopup() {
    checkOverlappable(POPUP);
    setNonModalMode(POPUP);
  }
  /**
   * Makes this window as highlited. The visual effect is the similar to the modal window, but, like
   * overlapped, it doesn't suspend (block) the execution at the server. In other words, it is more
   * like an overlapped window from the server side's viewpoint.
   */
  public void doHighlighted() {
    checkOverlappable(HIGHLIGHTED);
    setNonModalMode(HIGHLIGHTED);
  }
  /** Makes this window as embeded with other components (Default). */
  public void doEmbedded() {
    setNonModalMode(EMBEDDED);
  }
  /* Set non-modal mode. */
  private void setNonModalMode(int mode) {
    if (_mode != mode) {
      if (_mode == MODAL) leaveModal(mode);
      else {
        _mode = mode;
        smartUpdate("mode", modeToString(_mode));
      }
    }
    setVisible(true);
  }

  /** Set mode to MODAL and suspend this thread. */
  private void enterModal() throws InterruptedException {
    _mode = MODAL;
    smartUpdate("mode", modeToString(_mode));

    // no need to synchronized (_mutex) because no racing is possible
    Executions.wait(_mutex);
  }
  /** Resumes the suspendded thread and set mode to OVERLAPPED. */
  private void leaveModal(int mode) {
    _mode = mode;
    smartUpdate("mode", modeToString(_mode));

    Executions.notifyAll(_mutex);
  }

  private boolean isEventThreadEnabled(boolean attachedRequired) {
    Desktop desktop = getDesktop();
    if (desktop == null) {
      if (attachedRequired) throw new SuspendNotAllowedException("Not attached, " + this);

      final Execution exec = Executions.getCurrent();
      if (exec == null || (desktop = exec.getDesktop()) == null)
        return true; // assume enabled (safer)
    }
    return desktop.getWebApp().getConfiguration().isEventThreadEnabled();
  }

  /** Makes sure it is not draggable. */
  private void checkOverlappable(int mode) {
    if (!"false".equals(getDraggable()))
      throw new UiException(
          "Draggable window cannot be modal, overlapped, popup, or highlighted: " + this);

    if (mode == MODAL)
      for (Component comp = this; (comp = comp.getParent()) != null; )
        if (!comp.isVisible())
          throw new UiException(
              "One of its ancestors, "
                  + comp
                  + ", is not visible, so unable to be modal or highlighted");
  }

  /** Returns whether to show a close button on the title bar. */
  public boolean isClosable() {
    return _closable;
  }
  /**
   * Sets whether to show a close button on the title bar. If closable, a button is displayed and
   * the onClose event is sent if an user clicks the button.
   *
   * <p>Default: false.
   *
   * <p>You can intercept the default behavior by either overriding {@link #onClose}, or listening
   * the onClose event.
   *
   * <p>Note: the close button won't be displayed if no title or caption at all.
   */
  public void setClosable(boolean closable) {
    if (_closable != closable) {
      _closable = closable;
      smartUpdate("closable", closable); // re-init is required
    }
  }
  /** Returns whether the window is sizable. */
  public boolean isSizable() {
    return _sizable;
  }
  /**
   * Sets whether the window is sizable. If true, an user can drag the border to change the window
   * width.
   *
   * <p>Default: false.
   */
  public void setSizable(boolean sizable) {
    if (_sizable != sizable) {
      _sizable = sizable;
      smartUpdate("sizable", sizable);
    }
  }
  /**
   * Returns whether to show the shadow of an overlapped/popup/modal window. It is meaningless if it
   * is an embedded window.
   *
   * @since 3.6.0
   */
  public boolean isShadow() {
    return _shadow;
  }
  /**
   * Sets whether to show the shadow of an overlapped/popup/modal window. It is meaningless if it is
   * an embedded window.
   *
   * <p>Default: true.
   *
   * @since 3.6.0
   */
  public void setShadow(boolean shadow) {
    if (_shadow != shadow) {
      _shadow = shadow;
      smartUpdate("shadow", shadow);
    }
  }
  /**
   * Returns how to position the window at the client screen. It is meaningless if the embedded mode
   * is used.
   *
   * <p>Default: null which depends on {@link #getMode}: If overlapped or popup, {@link #setLeft}
   * and {@link #setTop} are assumed. If modal or highlighted, it is centered.
   */
  public String getPosition() {
    return _pos;
  }
  /**
   * Sets how to position the window at the client screen. It is meaningless if the embedded mode is
   * used.
   *
   * @param pos how to position. It can be null (the default), or a combination of the following
   *     values (by separating with comma).
   *     <dl>
   *       <dt>center
   *       <dd>Position the window at the center. {@link #setTop} and {@link #setLeft} are both
   *           ignored.
   *       <dt>nocenter
   *       <dd>Not to position the window at the center. A modal window, by default, will be
   *           position at the center. By specifying this value could prevent it and the real
   *           position depends on {@link #setTop} and {@link #setLeft} (since 5.0.4)
   *       <dt>left
   *       <dd>Position the window at the left edge. {@link #setLeft} is ignored.
   *       <dt>right
   *       <dd>Position the window at the right edge. {@link #setLeft} is ignored.
   *       <dt>top
   *       <dd>Position the window at the top edge. {@link #setTop} is ignored.
   *       <dt>bottom
   *       <dd>Position the window at the bottom edge. {@link #setTop} is ignored.
   *       <dt>parent
   *       <dd>Position the window relative to its parent. That is, the left and top ({@link
   *           #getTop} and {@link #getLeft}) is an offset to his parent's let-top corner. (since
   *           3.0.2)
   *     </dl>
   *     <p>For example, "left,center" means to position it at the center of the left edge.
   */
  public void setPosition(String pos) {
    // Note: we always update since the window might be dragged by an user
    _pos = pos;
    smartUpdate("position", pos);
  }

  /**
   * Process the onClose event sent when the close button is pressed.
   *
   * <p>Default: detach itself.
   */
  public void onClose() {
    detach();
  }
  /** Process the onModal event by making itself a modal window. */
  public void onModal() throws InterruptedException {
    doModal();
  }

  /** Returns the CSS style for the content block of the window. */
  public String getContentStyle() {
    return _cntStyle;
  }
  /**
   * Sets the CSS style for the content block of the window.
   *
   * <p>Default: null.
   */
  public void setContentStyle(String style) {
    if (!Objects.equals(_cntStyle, style)) {
      _cntStyle = style;
      smartUpdate("contentStyle", _cntStyle);
    }
  }

  /**
   * Returns the style class used for the content block.
   *
   * @see #setContentSclass
   */
  public String getContentSclass() {
    return _cntSclass;
  }
  /**
   * Sets the style class used for the content block.
   *
   * @see #getContentSclass
   * @since 3.0.0
   */
  public void setContentSclass(String scls) {
    if (!Objects.equals(_cntSclass, scls)) {
      _cntSclass = scls;
      smartUpdate("contentSclass", scls);
    }
  }

  /**
   * Makes this window as topmost. It has no effect if this window is embedded.
   *
   * @since 5.0.0
   */
  public void setTopmost() {
    smartUpdate("topmost", true);
  }

  // super
  protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer)
      throws java.io.IOException {
    super.renderProperties(renderer);

    render(renderer, "title", _title);
    render(renderer, "maximized", _maximized);
    render(renderer, "maximizable", _maximizable);
    render(renderer, "minimized", _minimized);
    render(renderer, "minimizable", _minimizable);
    render(renderer, "closable", _closable);
    render(renderer, "sizable", _sizable);
    render(renderer, "position", _pos);
    render(renderer, "contentStyle", _cntStyle);
    render(renderer, "contentSclass", _cntSclass);
    if (_minheight != 100) renderer.render("minheight", _minheight);
    if (_minwidth != 200) renderer.render("minwidth", _minwidth);
    if (!"none".equals(_border)) renderer.render("border", _border);
    if (!isShadow()) renderer.render("shadow", false);
    if (_mode != EMBEDDED) renderer.render("mode", modeToString(_mode));
    // render mode as the last property
  }

  public String getZclass() {
    return _zclass == null ? "z-window-" + getMode() : _zclass;
  }

  // -- Component --//
  public void beforeChildAdded(Component child, Component refChild) {
    if (child instanceof Caption) {
      if (_caption != null && _caption != child)
        throw new UiException("Only one caption is allowed: " + this);
    } else if (refChild instanceof Caption) {
      throw new UiException("caption must be the first child");
    }
    super.beforeChildAdded(child, refChild);
  }

  public boolean insertBefore(Component child, Component refChild) {
    if (child instanceof Caption) {
      refChild = getFirstChild();
      // always makes caption as the first child
      if (super.insertBefore(child, refChild)) {
        _caption = (Caption) child;
        return true;
      }
      return false;
    }
    return super.insertBefore(child, refChild);
  }

  public void onChildRemoved(Component child) {
    if (child instanceof Caption) _caption = null;
    super.onChildRemoved(child);
  }

  public void onPageDetached(Page page) {
    if (_mode == MODAL && getPage() == null) leaveModal(OVERLAPPED);
  }

  /**
   * Changes the visibility of the window.
   *
   * <p>Note if you turned on the event thread:<br>
   * If a modal dialog becomes invisible, the modal state will be ended automatically. In other
   * words, the mode ({@link #getMode}) will become {@link #OVERLAPPED} and the suspending thread is
   * resumed. In other words, the modal window ({@link #MODAL}) can not be invisible (while a window
   * in other modes could be invisible).
   *
   * <p>However, if the event thread is not enabled (default), there is no such limitation. In other
   * words, it remains the same mode when becoming invisible.
   */
  public boolean setVisible(boolean visible) {
    if (visible == isVisible()) return visible;
    _maximized = _minimized = false;
    return setVisible0(visible);
  }

  private boolean setVisible0(boolean visible) {
    if (!visible && _mode == MODAL) {
      // Hide first to avoid unpleasant effect
      super.setVisible(false);
      leaveModal(OVERLAPPED);
      return true;
    }
    return super.setVisible(visible);
  }

  // -- super --//
  public void setDraggable(String draggable) {
    if (_mode != EMBEDDED) {
      if (draggable != null && (draggable.length() > 0 && !"false".equals(draggable)))
        throw new UiException("Only embedded window could be draggable: " + this);
    }
    super.setDraggable(draggable);
  }

  // Cloneable//
  public Object clone() {
    final Window clone = (Window) super.clone();
    clone._mutex = new Mutex();
    if (clone._caption != null) clone.afterUnmarshal();
    return clone;
  }

  private void afterUnmarshal() {
    for (Iterator it = getChildren().iterator(); it.hasNext(); ) {
      final Object child = it.next();
      if (child instanceof Caption) {
        _caption = (Caption) child;
        break;
      }
    }
  }

  // Serializable//
  private synchronized void readObject(java.io.ObjectInputStream s)
      throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();
    afterUnmarshal();
  }

  // -- ComponentCtrl --//
  /**
   * Processes an AU request.
   *
   * <p>Default: in addition to what are handled by {@link XulElement#service}, it also handles
   * onOpen.
   *
   * @since 5.0.0
   */
  public void service(org.zkoss.zk.au.AuRequest request, boolean everError) {
    final String cmd = request.getCommand();
    if (cmd.equals(Events.ON_OPEN)) {
      OpenEvent evt = OpenEvent.getOpenEvent(request);
      setVisible(evt.isOpen());
      Events.postEvent(evt);
    } else if (cmd.equals(Events.ON_MAXIMIZE)) {
      MaximizeEvent evt = MaximizeEvent.getMaximizeEvent(request);
      setLeftDirectly(evt.getLeft());
      setTopDirectly(evt.getTop());
      setWidthDirectly(evt.getWidth());
      setHeightDirectly(evt.getHeight());
      _maximized = evt.isMaximized();
      if (_maximized) setVisibleDirectly(true);
      Events.postEvent(evt);
    } else if (cmd.equals(Events.ON_MINIMIZE)) {
      MinimizeEvent evt = MinimizeEvent.getMinimizeEvent(request);
      setLeftDirectly(evt.getLeft());
      setTopDirectly(evt.getTop());
      setWidthDirectly(evt.getWidth());
      setHeightDirectly(evt.getHeight());
      _minimized = evt.isMinimized();
      if (_minimized) {
        setVisibleDirectly(false);
        if (_mode == MODAL) leaveModal(OVERLAPPED);
      }
      Events.postEvent(evt);
    } else super.service(request, everError);
  }

  /**
   * Always return false.
   *
   * @since 3.6.2
   */
  public boolean isCollapsible() {
    return false;
  }
}
Esempio n. 9
0
/**
 * 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}.
 *
 * <p>Usage:
 *
 * <ol>
 *   <li>Implements a loader by extending from {@link ResourceLoader}.
 *   <li>Creates a resource cache ({@link ResourceCache}) by use of the loader in the previous step.
 *   <li>Invoke {@link #get} to load the resource.
 * </ol>
 *
 * @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> V get(
      ResourceCache<V> 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 directly
    // And, web  developer has to specify <welcome-file> 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) {
        try {
          return cache.get(new ResourceInfo(path, new File(flnm), 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;
  }
}
Esempio n. 10
0
/**
 * @author Georgi Rahnev (http://github.com/rahnev)
 * @version $Id$
 */
public abstract class ServerPushClient extends Thread {

  private static final Log _log = Log.lookup(ServerPushClient.class);
  private final Client _client;
  private boolean _exit = false;

  public abstract void nextIteration() throws Exception;

  protected ServerPushClient(Client cleint) {
    super();
    this.setName(this.getClass().getSimpleName() + " for desktop " + cleint.getDesktop().getId());
    _client = cleint;
    _client
        .getDesktop()
        .addListener(
            new DesktopCleanup() {

              @Override
              public void cleanup(Desktop desktop) throws Exception {
                exit();
              }
            });
  }

  public Client getCleint() {
    return _client;
  }

  public void enter() {
    start();
  }

  public void exit() {
    _exit = true;
  }

  @Override
  public void run() {
    while (!_exit) {
      try {
        nextIteration();
      } catch (Exception ex) {
        _log.error(ex);
        exit();
      }
    }
  }

  public static ServerPushClient create(Client client) throws EmulationException {
    Client activeClient = TestUpdateServlet.getActiveClient();

    TestUpdateServlet.enter(client);
    ServerPush serverPush = null;
    if (!client.getDesktop().isServerPushEnabled()) {
      client.getDesktop().enableServerPush(true);
      serverPush = ((DesktopImpl) client.getDesktop()).getServerPush();
      client.getDesktop().enableServerPush(false);
    } else {
      serverPush = ((DesktopImpl) client.getDesktop()).getServerPush();
    }
    if (serverPush == null) {
      return new NoServerPushClientEmulator(client);
    }
    if (serverPush.getClass().getName().equals("org.zkoss.zkmax.ui.comet.CometServerPush")) {
      return new CometClientEmulator(client);
    }
    if (serverPush instanceof PollingServerPush) {
      return new PollingClientEmulator(client, 500);
    }

    if (activeClient != null) {
      TestUpdateServlet.leave(activeClient);
    }
    throw new AssertionError("Unsupported server push model " + serverPush.getClass().getName());
  }
}
Esempio n. 11
0
/**
 * Utilities to access {@link Component}.
 *
 * @author tomyeh
 */
public class Components {
  private static final Log log = Log.lookup(Components.class);
  private static final Log _zklog = Log.lookup("org.zkoss.zk.log");

  protected Components() {}

  /**
   * Returns the parent of the ID space, or null if not found.
   *
   * @since 5.0.0
   */
  public static IdSpace getParentIdSpace(IdSpace idspace) {
    if (idspace instanceof Component) {
      final Component c = (Component) idspace;
      final Component p = c.getParent();
      return p != null ? p.getSpaceOwner() : c.getPage();
    }
    return null;
  }

  /**
   * Sorts the components in the list.
   *
   * <p>Note: you cannot use {@link Collections#sort} to sort {@link Component#getChildren} because
   * Collections.sort might cause some replicated item in the list.
   *
   * @see #sort(List, int, int, Comparator)
   */
  public static void sort(List<? extends Component> list, Comparator<? super Component> cpr) {
    sort(list, 0, list.size(), cpr);
  }

  /**
   * Replaces a component with another.
   *
   * @param oldc the component to remove.
   * @param newc the component to add
   * @exception IllegalArgumentException if oldc's parent and page are both null.
   * @since 3.5.2
   */
  public static void replace(Component oldc, Component newc) {
    final Component p = oldc.getParent(), sib = oldc.getNextSibling();
    if (p != null) {
      oldc.detach();
      p.insertBefore(newc, sib);
    } else {
      final Page page = oldc.getPage();
      if (page == null) throw new IllegalArgumentException("Neither child nor attached, " + oldc);
      oldc.detach();
      if (newc.getParent() != null) newc.detach();
      newc.setPageBefore(page, sib);
    }
  }
  /**
   * Replaces all children of the specified component. It is the same as
   *
   * <pre><code>parent.getChildren().clear();
   * parent.getChildren().addAll(newChildren);
   * </code></pre>
   *
   * @since 3.5.2
   */
  public static void replaceChildren(Component parent, Collection<Component> newChildren) {
    final Collection<Component> children = parent.getChildren();
    children.clear();
    children.addAll(newChildren);
  }
  /**
   * Sorts the components in the list.
   *
   * @param list the list to be sorted
   * @param from the index of the first element (inclusive) to be sorted
   * @param to the index of the last element (exclusive) to be sorted
   * @param cpr the comparator to determine the order of the list.
   * @since 3.5.0
   */
  public static void sort(
      List<? extends Component> list, int from, int to, Comparator<? super Component> cpr) {
    final Component ary[] = CollectionsX.toArray(list, new Component[0], from, to);
    Arrays.sort(ary, cpr);

    ListIterator<? extends Component> it = list.listIterator(from);
    int j = 0, k = to - from;
    for (; it.hasNext() && --k >= 0; ++j) {
      if (it.next() != ary[j]) {
        it.remove();

        if (it.hasNext() && --k >= 0) {
          if (it.next() == ary[j]) continue;
          it.previous();
          ++k;
        }
        break;
      }
    }
    while (it.hasNext() && --k >= 0) {
      it.next();
      it.remove();
    }
    for (; j < ary.length; ++j) add(list, from + j, ary[j]);
  }

  @SuppressWarnings("unchecked")
  private static void add(List list, int index, Object o) { // to minimize the unchecked range
    list.add(index, o);
  }

  /**
   * Returns the root component of the specified one. Notice that it could return <code>comp</code>,
   * if it is already a root component (or it is null).
   *
   * @since 3.6.3
   */
  public static Component getRoot(Component comp) {
    if (comp == null) return null;
    for (; ; ) {
      final Component p = comp.getParent();
      if (p == null) return comp;
      comp = p;
    }
  }

  /**
   * Tests whether node1 is an ancessor of node 2. If node1 and node2 is the same, true is returned.
   */
  public static boolean isAncestor(Component node1, Component node2) {
    for (; node2 != null; node2 = node2.getParent()) {
      if (node1 == node2) return true;
    }
    return false;
  }

  /**
   * Removes all children of the specified component. It is the same as <code>
   * comp.getChildren().clear()</code>.
   */
  public static void removeAllChildren(Component comp) {
    comp.getChildren().clear();
  }

  /**
   * Returns the component definition of the specified class in all language of the specified
   * device, or null if not found
   *
   * @param deviceType the device type ({@link org.zkoss.zk.device.Device}), such as ajax. It cannot
   *     be null.
   * @param cls the implementation class of the component.
   * @since 5.0.0
   */
  public static final ComponentDefinition getDefinitionByDeviceType(String deviceType, Class cls) {
    for (LanguageDefinition ld : LanguageDefinition.getByDeviceType(deviceType)) {
      try {
        return ld.getComponentDefinition(cls);
      } catch (DefinitionNotFoundException ex) { // ignore
      }
    }
    return null;
  }

  /**
   * Returns whether this component is real visible (all its parents are visible).
   *
   * <p>Note: true is returned if comp is null. In other words, it can be used to examine parent's
   * real visibity even if it is a root component, such as <code>
   * Components.isRealVisible(getParent())</code>.
   *
   * @see Component#isVisible
   */
  public static boolean isRealVisible(Component comp) {
    for (; comp != null; comp = comp.getParent()) if (!comp.isVisible()) return false;
    return true;
  }
  /**
   * Returns a collection of visible children.
   *
   * <p>The performance of the returned collection's size() is NO GOOD.
   */
  public static Collection<Component> getVisibleChildren(Component comp) {
    final Collection<Component> children = comp.getChildren();
    return new AbstractCollection<Component>() {
      public int size() {
        int size = 0;
        for (Component c : children) {
          if (c.isVisible()) ++size;
        }
        return size;
      }

      public Iterator<Component> iterator() {
        return new Iterator<Component>() {
          final Iterator<Component> _it = children.iterator();
          Component _next;

          public boolean hasNext() {
            if (_next != null) return true;
            _next = getNextVisible(false);
            return _next != null;
          }

          public Component next() {
            if (_next != null) {
              final Component c = _next;
              _next = null;
              return c;
            }
            return getNextVisible(true);
          }

          public void remove() {
            throw new UnsupportedOperationException();
          }

          private Component getNextVisible(boolean blind) {
            while (blind || _it.hasNext()) {
              final Component c = (Component) _it.next();
              if (c.isVisible()) return c;
            }
            return null;
          }
        };
      }
    };
  }

  /**
   * Converts a string to an integer that can be used to access {@link
   * Component#getAttribute(String, int)}
   */
  public static final int getScope(String scope) {
    if ("component".equals(scope)) return Component.COMPONENT_SCOPE;
    if ("space".equals(scope)) return Component.SPACE_SCOPE;
    if ("page".equals(scope)) return Component.PAGE_SCOPE;
    if ("desktop".equals(scope)) return Component.DESKTOP_SCOPE;
    if ("session".equals(scope)) return Component.SESSION_SCOPE;
    if ("application".equals(scope)) return Component.APPLICATION_SCOPE;
    if ("request".equals(scope)) return Component.REQUEST_SCOPE;
    throw new IllegalArgumentException("Unknown scope: " + scope);
  }
  /**
   * Converts an integer to the string representing the scope.
   *
   * @param scope one of {@link Component#COMPONENT_SCOPE}, {@link Component#SPACE_SCOPE}, {@link
   *     Component#PAGE_SCOPE}, {@link Component#DESKTOP_SCOPE}, {@link Component#SESSION_SCOPE},
   *     {@link Component#REQUEST_SCOPE}, and {@link Component#APPLICATION_SCOPE}.
   */
  public static final String scopeToString(int scope) {
    switch (scope) {
      case Component.COMPONENT_SCOPE:
        return "component";
      case Component.SPACE_SCOPE:
        return "space";
      case Component.PAGE_SCOPE:
        return "page";
      case Component.DESKTOP_SCOPE:
        return "desktop";
      case Component.SESSION_SCOPE:
        return "session";
      case Component.APPLICATION_SCOPE:
        return "application";
      case Component.REQUEST_SCOPE:
        return "request";
    }
    throw new IllegalArgumentException("Unknown scope: " + scope);
  }

  /**
   * Converts a component to a path (relavant to another component). It is usefully to implement a
   * serializable component that contains a reference to another component. In this case, we can not
   * serializes the reference directly (otherwise, another component will be created, when
   * deserialized).
   *
   * <p>Rather, it is better to store the path related, and then restore it back to a component by
   * calling {@link #pathToComponent}.
   *
   * @param comp the component to be converted to path. It cannot be null.
   * @param ref the component used to generated the path from. It cannot be null.
   * @return the path. Notice that you have to use {@link #pathToComponent} to convert it back.
   * @exception UnsupportedOperationException if we cannot find a path to the component to write.
   * @since 3.0.0
   */
  public static final String componentToPath(Component comp, Component ref) {
    // Implementation Note:
    // The path being written is a bit different to Path, if ref
    // is not an space owner
    // For example, if comp is the space owner, "" is written.
    // If comp is the same as ref, "." is written.
    if (comp == null) {
      return null;
    } else if (comp == ref) {
      return ".";
    } else {
      final String id = comp.getId();
      if (!(comp instanceof IdSpace) && id.length() == 0)
        throw new UnsupportedOperationException(
            "comp must be assigned with ID or a space owner: " + comp);

      final StringBuffer sb = new StringBuffer(128);
      for (IdSpace space = ref.getSpaceOwner(); ; ) {
        if (comp == space) {
          return sb.toString(); // could be ""
          // we don't generate id to make it work even if
          // its ID is changed
        } else if (space.getFellowIfAny(id) == comp) {
          if (sb.length() > 0) sb.append('/');
          return sb.append(id).toString();
        }

        if (sb.length() > 0) sb.append('/');
        sb.append("..");

        final Component parent =
            space instanceof Component ? ((Component) space).getParent() : null;
        if (parent == null)
          throw new UnsupportedOperationException("Unable to locate " + comp + " from " + ref);
        space = parent.getSpaceOwner();
      }
    }
  }
  /**
   * Converts a path, generated by {@link #componentToPath}, to a component.
   *
   * @param ref the component used to generated the path from. It cannot be null. It is the same as
   *     the one when calling {@link #componentToPath}.
   * @since 3.0.0
   */
  public static final Component pathToComponent(String path, Component ref) {
    if (path == null) {
      return null;
    } else if (".".equals(path)) {
      return ref;
    } else if ("".equals(path)) {
      final IdSpace owner = ref.getSpaceOwner();
      if (!(owner instanceof Component))
        throw new IllegalStateException("The component is moved after serialized: " + ref);
      return (Component) owner;
    }
    return Path.getComponent(ref.getSpaceOwner(), path);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireFellows(IdSpace idspace, Object controller) {
    ConventionWires.wireFellows(idspace, controller);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireFellows(IdSpace idspace, Object controller, char separator) {
    ConventionWires.wireFellows(idspace, controller, separator);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireFellows(
      IdSpace idspace,
      Object controller,
      char separator,
      boolean ignoreZScript,
      boolean ignoreXel) {
    ConventionWires.wireFellows(idspace, controller, separator, ignoreZScript, ignoreXel);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireVariables(Component comp, Object controller) {
    ConventionWires.wireVariables(comp, controller);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireVariables(Component comp, Object controller, char separator) {
    ConventionWires.wireVariables(comp, controller, separator);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireVariables(
      Component comp, Object controller, char separator, boolean ignoreZScript, boolean ignoreXel) {
    ConventionWires.wireVariables(comp, controller, separator, ignoreZScript, ignoreXel);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireVariables(Page page, Object controller) {
    ConventionWires.wireVariables(page, controller);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireVariables(Page page, Object controller, char separator) {
    ConventionWires.wireVariables(page, controller, separator);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireVariables(
      Page page, Object controller, char separator, boolean ignoreZScript, boolean ignoreXel) {
    ConventionWires.wireVariables(page, controller, separator, ignoreZScript, ignoreXel);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireController(Component comp, Object controller) {
    ConventionWires.wireController(comp, controller);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireController(Component comp, Object controller, char separator) {
    ConventionWires.wireController(comp, controller, separator);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireController(
      Component comp, Object controller, char separator, boolean ignoreZScript, boolean ignoreXel) {
    ConventionWires.wireController(comp, controller, separator);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static final void wireImplicit(Component comp, Object controller) {
    ConventionWires.wireImplicit(comp, controller);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static void addForwards(Component comp, Object controller) {
    ConventionWires.addForwards(comp, controller);
  }
  /** @deprecated As of release 6.0.0, replaced with {@link ConventionWires}. */
  public static void addForwards(Component comp, Object controller, char separator) {
    ConventionWires.addForwards(comp, controller, separator);
  }

  /**
   * Returns whether the given id is an implicit ZK object id.
   *
   * @param id Component id
   * @return whether the given name is a implicit object.
   * @since 3.5.2
   */
  public static boolean isImplicit(String id) {
    return IMPLICIT_NAMES.contains(id);
  }
  /**
   * Retuns a readonly collection of the names of the implicit objects.
   *
   * @since 6.0.0
   */
  public static Collection<String> getImplicitNames() {
    return IMPLICIT_NAMES;
  }

  private static final Set<String> IMPLICIT_NAMES = new HashSet<String>();

  static {
    final String[] names = {
      "application",
      "applicationScope",
      "arg",
      "componentScope",
      "desktop",
      "desktopScope",
      "execution",
      "event", // since 3.6.1, #bug 2681819: normal page throws exception after installed zkspring
      "self",
      "session",
      "sessionScope",
      "spaceOwner",
      "spaceScope",
      "page",
      "pageScope",
      "requestScope",
      "param"
    };
    for (int j = 0; j < names.length; ++j) IMPLICIT_NAMES.add(names[j]);
  }

  /**
   * Retuns the implicit object of the specified name, or null if not found.
   *
   * <p>Notice that it does check for the current scope ({@link
   * org.zkoss.zk.ui.ext.Scopes#getCurrent}). Rather, {@link org.zkoss.zk.ui.ext.Scopes#getImplicit}
   * depends on this method.
   *
   * @param page the page. If page is null and comp is not, comp.getPage() is assumed
   * @see org.zkoss.zk.ui.ext.Scopes#getImplicit
   * @since 5.0.0
   */
  public static Object getImplicit(Page page, Component comp, String name) {
    if (comp != null && page == null) page = getCurrentPage(comp);

    if ("log".equals(name)) return _zklog;
    if ("self".equals(name)) return comp != null ? comp : (Object) page;
    if ("spaceOwner".equals(name)) return comp != null ? comp.getSpaceOwner() : (Object) page;
    if ("page".equals(name)) return page;
    if ("desktop".equals(name)) return comp != null ? getDesktop(comp) : page.getDesktop();
    if ("session".equals(name))
      return comp != null ? getSession(comp) : page.getDesktop().getSession();
    if ("application".equals(name))
      return comp != null ? getWebApp(comp) : page.getDesktop().getWebApp();
    if ("componentScope".equals(name))
      return comp != null ? comp.getAttributes() : Collections.EMPTY_MAP;
    if ("spaceScope".equals(name)) {
      final Scope scope = comp != null ? (Scope) comp.getSpaceOwner() : (Scope) page;
      return scope != null ? scope.getAttributes() : Collections.EMPTY_MAP;
    }
    if ("pageScope".equals(name))
      return page != null ? page.getAttributes() : Collections.EMPTY_MAP;
    if ("desktopScope".equals(name)) {
      final Desktop dt = comp != null ? getDesktop(comp) : page.getDesktop();
      return dt != null ? dt.getAttributes() : Collections.EMPTY_MAP;
    }
    if ("sessionScope".equals(name)) {
      final Session sess = comp != null ? getSession(comp) : page.getDesktop().getSession();
      return sess != null ? sess.getAttributes() : Collections.EMPTY_MAP;
    }
    if ("applicationScope".equals(name)) {
      final WebApp app = comp != null ? getWebApp(comp) : page.getDesktop().getWebApp();
      return app != null ? app.getAttributes() : Collections.EMPTY_MAP;
    }
    if ("requestScope".equals(name)) return REQUEST_SCOPE_PROXY;
    if ("execution".equals(name)) return EXECUTION_PROXY;
    if ("arg".equals(name)) {
      final Execution exec = Executions.getCurrent();
      return exec != null ? exec.getArg() : null;
      // bug 2937096: composer.arg shall be statically wired
      // arg is a Map prepared by application developer, so can be wired statically
    }
    if ("param".equals(name)) {
      final Execution exec = Executions.getCurrent();
      return exec != null ? exec.getParameterMap() : null;
      // bug 2945974: composer.param shall be statically wired
      // Note that request parameter is prepared by servlet container, you shall not
      // copy the reference to this map; rather, you shall clone the key-value pair one-by-one.
    }
    // 20090314, Henri Chen: No way to suppport "event" with an event proxy because
    // org.zkoss.zk.Event is not an interface
    return null;
  }
  /**
   * Retuns the implicit object of the specified name, or null if not found.
   *
   * <p>It is the same as getImplicit(null, comp, name).
   *
   * @since 3.6.0
   */
  public static Object getImplicit(Component comp, String name) {
    return getImplicit(null, comp, name);
  }
  /**
   * Retuns the implicit object of the specified name, or null if not found.
   *
   * <p>It is the same as getImplicit(page, null, name).
   *
   * @since 3.6.0
   */
  public static Object getImplicit(Page page, String name) {
    return getImplicit(page, null, name);
  }

  private static Desktop getDesktop(Component comp) {
    final Desktop dt = comp.getDesktop();
    if (dt != null) return dt;
    final Execution exec = Executions.getCurrent();
    return exec != null ? exec.getDesktop() : null;
  }

  private static WebApp getWebApp(Component comp) {
    final Desktop dt = getDesktop(comp);
    return dt != null ? dt.getWebApp() : null;
  }

  private static Session getSession(Component comp) {
    final Desktop dt = getDesktop(comp);
    return dt != null ? dt.getSession() : null;
  }
  /**
   * Returns the page of the give component, or the current page if the component is null or it
   * doesn't belong to any page. The current page is retrieved by {@link
   * ExecutionCtrl#getCurrentPage} or the current execution. This method returns null if no
   * execution or no current page at all.
   *
   * @param comp the component to retrieve the page. Ignored if null.
   * @since 6.0.0
   */
  public static Page getCurrentPage(Component comp) {
    if (comp != null) {
      Page page = comp.getPage();
      if (page != null) return page;
    }

    final Execution exec = Executions.getCurrent();
    return exec != null ? ((ExecutionCtrl) exec).getCurrentPage() : null;
  }

  /** Execution Proxy */
  public static final Exec EXECUTION_PROXY = new Exec();

  /** Request Scope Proxy */
  public static final RequestScope REQUEST_SCOPE_PROXY = new RequestScope();

  // Proxy to read current execution
  private static class Exec implements Execution {
    private static final Execution exec() {
      return Executions.getCurrent();
    }

    public void addAuResponse(AuResponse response) {
      exec().addAuResponse(response);
    }

    public void addAuResponse(String key, AuResponse response) {
      exec().addAuResponse(key, response);
    }

    public Component createComponents(PageDefinition pagedef, Component parent, Map<?, ?> arg) {
      return exec().createComponents(pagedef, parent, arg);
    }

    public Component createComponents(String uri, Component parent, Map<?, ?> arg) {
      return exec().createComponents(uri, parent, arg);
    }

    public Component createComponentsDirectly(
        String content, String extension, Component parent, Map<?, ?> arg) {
      return exec().createComponentsDirectly(content, extension, parent, arg);
    }

    public Component createComponentsDirectly(
        Document content, String extension, Component parent, Map<?, ?> arg) {
      return exec().createComponentsDirectly(content, extension, parent, arg);
    }

    public Component createComponentsDirectly(
        Reader reader, String extension, Component parent, Map<?, ?> arg) throws IOException {
      return exec().createComponentsDirectly(reader, extension, parent, arg);
    }

    public Component createComponents(
        PageDefinition pagedef,
        Component parent,
        Component insertBefore,
        VariableResolver resolver) {
      return exec().createComponents(pagedef, parent, insertBefore, resolver);
    }

    public Component createComponents(
        String uri, Component parent, Component insertBefore, VariableResolver resolver) {
      return exec().createComponents(uri, parent, insertBefore, resolver);
    }

    public Component createComponentsDirectly(
        String content,
        String extension,
        Component parent,
        Component insertBefore,
        VariableResolver resolver) {
      return exec().createComponentsDirectly(content, extension, parent, insertBefore, resolver);
    }

    public Component createComponentsDirectly(
        Document content,
        String extension,
        Component parent,
        Component insertBefore,
        VariableResolver resolver) {
      return exec().createComponentsDirectly(content, extension, parent, insertBefore, resolver);
    }

    public Component createComponentsDirectly(
        Reader reader,
        String extension,
        Component parent,
        Component insertBefore,
        VariableResolver resolver)
        throws IOException {
      return exec().createComponentsDirectly(reader, extension, parent, insertBefore, resolver);
    }

    public Component[] createComponents(PageDefinition pagedef, Map<?, ?> arg) {
      return exec().createComponents(pagedef, arg);
    }

    public Component[] createComponents(String uri, Map<?, ?> arg) {
      return exec().createComponents(uri, arg);
    }

    public Component[] createComponentsDirectly(String content, String extension, Map<?, ?> arg) {
      return exec().createComponentsDirectly(content, extension, arg);
    }

    public Component[] createComponentsDirectly(Document content, String extension, Map<?, ?> arg) {
      return exec().createComponentsDirectly(content, extension, arg);
    }

    public Component[] createComponentsDirectly(Reader reader, String extension, Map<?, ?> arg)
        throws IOException {
      return exec().createComponentsDirectly(reader, extension, arg);
    }

    public String encodeURL(String uri) {
      return exec().encodeURL(uri);
    }

    public Object evaluate(Component comp, String expr, Class expectedType) {
      return exec().evaluate(comp, expr, expectedType);
    }

    public Object evaluate(Page page, String expr, Class expectedType) {
      return exec().evaluate(page, expr, expectedType);
    }

    public void forward(Writer writer, String page, Map<String, ?> params, int mode)
        throws IOException {
      exec().forward(writer, page, params, mode);
    }

    public void forward(String page) throws IOException {
      exec().forward(page);
    }

    public Map<?, ?> getArg() {
      return exec().getArg();
    }

    public Object getAttribute(String name) {
      return exec().getAttribute(name);
    }

    public boolean hasAttribute(String name) {
      return exec().hasAttribute(name);
    }

    public Object getAttribute(String name, boolean recurse) {
      return exec().getAttribute(name, recurse);
    }

    public boolean hasAttribute(String name, boolean recurse) {
      return exec().hasAttribute(name, recurse);
    }

    public Object setAttribute(String name, Object value, boolean recurse) {
      return exec().setAttribute(name, value, recurse);
    }

    public Object removeAttribute(String name, boolean recurse) {
      return exec().removeAttribute(name, recurse);
    }

    public boolean addScopeListener(ScopeListener listener) {
      return exec().addScopeListener(listener);
    }

    public boolean removeScopeListener(ScopeListener listener) {
      return exec().removeScopeListener(listener);
    }

    public Map<String, Object> getAttributes() {
      return exec().getAttributes();
    }

    public String getContextPath() {
      return exec().getContextPath();
    }

    public Desktop getDesktop() {
      return exec().getDesktop();
    }

    public Evaluator getEvaluator(Page page, Class<? extends ExpressionFactory> expfcls) {
      return exec().getEvaluator(page, expfcls);
    }

    public Evaluator getEvaluator(Component comp, Class<? extends ExpressionFactory> expfcls) {
      return exec().getEvaluator(comp, expfcls);
    }

    public String getLocalAddr() {
      return exec().getLocalAddr();
    }

    public String getLocalName() {
      return exec().getLocalName();
    }

    public int getLocalPort() {
      return exec().getLocalPort();
    }

    public Object getNativeRequest() {
      return exec().getNativeRequest();
    }

    public Object getNativeResponse() {
      return exec().getNativeResponse();
    }

    public PageDefinition getPageDefinition(String uri) {
      return exec().getPageDefinition(uri);
    }

    public PageDefinition getPageDefinitionDirectly(String content, String extension) {
      return exec().getPageDefinitionDirectly(content, extension);
    }

    public PageDefinition getPageDefinitionDirectly(Document content, String extension) {
      return exec().getPageDefinitionDirectly(content, extension);
    }

    public PageDefinition getPageDefinitionDirectly(Reader reader, String extension)
        throws IOException {
      return exec().getPageDefinitionDirectly(reader, extension);
    }

    public String getParameter(String name) {
      return exec().getParameter(name);
    }

    public Map<String, String[]> getParameterMap() {
      return exec().getParameterMap();
    }

    public String[] getParameterValues(String name) {
      return exec().getParameterValues(name);
    }

    public String getRemoteAddr() {
      return exec().getRemoteAddr();
    }

    public String getRemoteHost() {
      return exec().getRemoteHost();
    }

    public String getRemoteUser() {
      return exec().getRemoteUser();
    }

    public String getServerName() {
      return exec().getServerName();
    }

    public int getServerPort() {
      return exec().getServerPort();
    }

    public String getScheme() {
      return exec().getScheme();
    }

    public String getUserAgent() {
      return exec().getUserAgent();
    }

    public Principal getUserPrincipal() {
      return exec().getUserPrincipal();
    }

    public VariableResolver getVariableResolver() {
      return exec().getVariableResolver();
    }

    public void include(Writer writer, String page, Map<String, ?> params, int mode)
        throws IOException {
      exec().include(writer, page, params, mode);
    }

    public void include(String page) throws IOException {
      exec().include(page);
    }

    public boolean isAsyncUpdate(Page page) {
      return exec().isAsyncUpdate(page);
    }

    @Override
    public Double getBrowser(String name) {
      return exec().getBrowser(name);
    }

    @Override
    public String getBrowser() {
      return exec().getBrowser();
    }

    /** @deprecated As of release 6.0.0, replaced with {@link #getBrowser(String)}. */
    public boolean isBrowser() {
      return exec().isBrowser();
    }
    /** @deprecated As of release 6.0.0, replaced with {@link #getBrowser(String)}. */
    public boolean isBrowser(String type) {
      return exec().isBrowser(type);
    }
    /** @deprecated As of release 6.0.0, replaced with {@link #getBrowser(String)}. */
    public boolean isExplorer() {
      return exec().isExplorer();
    }
    /** @deprecated As of release 6.0.0, replaced with {@link #getBrowser(String)}. */
    public boolean isExplorer7() {
      return exec().isExplorer7();
    }
    /** @deprecated As of release 6.0.0, replaced with {@link #getBrowser(String)}. */
    public boolean isOpera() {
      return exec().isOpera();
    }
    /** @deprecated As of release 6.0.0, replaced with {@link #getBrowser(String)}. */
    public boolean isGecko() {
      return exec().isGecko();
    }
    /** @deprecated As of release 6.0.0, replaced with {@link #getBrowser(String)}. */
    public boolean isGecko3() {
      return exec().isGecko3();
    }
    /** @deprecated As of release 6.0.0, replaced with {@link #getBrowser(String)}. */
    public boolean isHilDevice() {
      return exec().isHilDevice();
    }
    /** @deprecated As of release 6.0.0, replaced with {@link #getBrowser(String)}. */
    public boolean isSafari() {
      return exec().isSafari();
    }
    /** @deprecated As of release 6.0.0, replaced with {@link #getBrowser(String)}. */
    public boolean isRobot() {
      return exec().isRobot();
    }

    public boolean isForwarded() {
      return exec().isForwarded();
    }

    public boolean isIncluded() {
      return exec().isIncluded();
    }

    public boolean isUserInRole(String role) {
      return exec().isUserInRole(role);
    }

    public boolean isVoided() {
      return exec().isVoided();
    }

    public void popArg() {
      exec().popArg();
    }

    public void postEvent(Event evt) {
      exec().postEvent(evt);
    }

    public void postEvent(int priority, Event evt) {
      exec().postEvent(priority, evt);
    }

    public void postEvent(int priority, Component realTarget, Event evt) {
      exec().postEvent(priority, realTarget, evt);
    }

    public void pushArg(Map<?, ?> arg) {
      exec().pushArg(arg);
    }

    public Object removeAttribute(String name) {
      return exec().removeAttribute(name);
    }

    public void sendRedirect(String uri) {
      exec().sendRedirect(uri);
    }

    public void sendRedirect(String uri, String target) {
      exec().sendRedirect(uri, target);
    }

    public Object setAttribute(String name, Object value) {
      return exec().setAttribute(name, value);
    }

    public void setVoided(boolean voided) {
      exec().setVoided(voided);
    }

    public String toAbsoluteURI(String uri, boolean skipInclude) {
      return exec().toAbsoluteURI(uri, skipInclude);
    }

    public void addResponseHeader(String name, String value) {
      exec().addResponseHeader(name, value);
    }

    public void addResponseHeader(String name, Date value) {
      exec().addResponseHeader(name, value);
    }

    public boolean containsResponseHeader(String name) {
      return exec().containsResponseHeader(name);
    }

    public String getHeader(String name) {
      return exec().getHeader(name);
    }

    public Iterable<String> getHeaderNames() {
      return exec().getHeaderNames();
    }

    public Iterable<String> getHeaders(String name) {
      return exec().getHeaders(name);
    }

    public void setResponseHeader(String name, String value) {
      exec().setResponseHeader(name, value);
    }

    public void setResponseHeader(String name, Date value) {
      exec().setResponseHeader(name, value);
    }

    public Session getSession() {
      return exec().getSession();
    }

    public String locate(String path) {
      return exec().locate(path);
    }

    public boolean addVariableResolver(VariableResolver resolver) {
      return exec().addVariableResolver(resolver);
    }

    public boolean removeVariableResolver(VariableResolver resolver) {
      return exec().removeVariableResolver(resolver);
    }

    public boolean hasVariableResolver(VariableResolver resolver) {
      return exec().hasVariableResolver(resolver);
    }

    public String toString() {
      return Objects.toString(exec());
    }

    public int hashCode() {
      return Objects.hashCode(exec());
    }

    public boolean equals(Object o) {
      if (o instanceof Exec) return Objects.equals(exec(), ((Exec) o).exec());
      return Objects.equals(exec(), o);
    }

    @Override
    public void log(String msg) {
      exec().log(msg);
    }

    @Override
    public void log(String msg, Throwable ex) {
      exec().log(msg, ex);
    }
  }

  // Proxy to read current requestScope
  private static class RequestScope implements Map<String, Object> {
    protected Map<String, Object> req() {
      return Executions.getCurrent().getAttributes();
    }

    public void clear() {
      req().clear();
    }

    public boolean containsKey(Object key) {
      return req().containsKey(key);
    }

    public boolean containsValue(Object value) {
      return req().containsValue(value);
    }

    public Set<Map.Entry<String, Object>> entrySet() {
      return req().entrySet();
    }

    public Object get(Object key) {
      return req().get(key);
    }

    public boolean isEmpty() {
      return req().isEmpty();
    }

    public Set<String> keySet() {
      return req().keySet();
    }

    public Object put(String key, Object value) {
      return req().put(key, value);
    }

    public void putAll(Map<? extends String, ? extends Object> arg0) {
      req().putAll(arg0);
    }

    public Object remove(Object key) {
      return req().remove(key);
    }

    public int size() {
      return req().size();
    }

    public Collection<Object> values() {
      return req().values();
    }

    public String toString() {
      return Objects.toString(req());
    }

    public int hashCode() {
      return Objects.hashCode(req());
    }

    public boolean equals(Object o) {
      if (o instanceof RequestScope) return Objects.equals(req(), ((RequestScope) o).req());
      return Objects.equals(req(), o);
    }
  }
}
Esempio n. 12
0
/**
 * Track Binding CRUD and dependent tracking management.
 *
 * @author henrichen
 * @since 6.0.0
 */
public class BindUiLifeCycle implements UiLifeCycle {

  static final Log log = Log.lookup(BindUiLifeCycle.class);
  private static Extension _ext;

  public void afterComponentAttached(Component comp, Page page) {
    if (comp.getDesktop() != null) {
      // check if this component already binded
      Binder selfBinder = BinderUtil.getBinder(comp);
      if (selfBinder == null) {
        // check if parent exists any binder
        Binder parentBinder = BinderUtil.getBinder(comp, true);

        // post event to let the binder to handle binding later
        if (parentBinder != null && (parentBinder instanceof BinderImpl)) {
          // ZK-603, ZK-604, ZK-605
          // register internal ON_BIND_INIT event listener to delay the timing of init and loading
          // bindings
          comp.addEventListener(
              10000,
              BinderImpl.ON_BIND_INIT,
              new EventListener<Event>() {

                public void onEvent(Event event) throws Exception {
                  final Component comp = event.getTarget();
                  comp.removeEventListener(BinderImpl.ON_BIND_INIT, this);
                  // ZK-611 have wrong binding on a removed treecell in a template
                  // if it was detached, ignore it
                  if (comp.getPage() == null) {
                    return;
                  }

                  final Binder innerBinder = BinderUtil.getBinder(comp);
                  if (innerBinder
                      != null) { // it was already handled by innerBinder, ignore it
                    return;
                  }

                  // ZK-1640 command send 2 wrong ViewModel
                  // check if there any parent binder again, don't use out-side parentBinder, it is
                  // not correct
                  Binder binder = BinderUtil.getBinder(comp, true);
                  if (binder == null) {
                    return;
                  }

                  // ZK-1699 Performance issue ZK-Bind getters are called multiple times
                  // check if it is handling, if yes then skip to evaluate it.
                  if (getExtension().isLifeCycleHandling(comp)) {
                    return;
                  }

                  if (binder instanceof AnnotateBinder) {
                    new AnnotateBinderHelper(binder).initComponentBindings(comp);
                  }

                  // ZK-1699, mark the comp and it's children are handling.
                  // note:mark handing before load, because of some load will change(create or
                  // reset) the children structure
                  // (consider F00769.zul if you bind to tree open , it will load children in
                  // loadComponent)
                  getExtension().markLifeCycleHandling(comp);

                  binder.loadComponent(comp, true);

                  // [Dennis,20120925], this code was added when fixing issue zk-739,
                  // but , inside binder.initComponentBindings, it shall do this already, I am not
                  // sure why.
                  if (comp.getAttribute(BinderImpl.VAR) != null)
                    BinderUtil.markHandling(comp, binder);
                }
              });
          // post ON_BIND_INIT event
          Events.postEvent(new Event(BinderImpl.ON_BIND_INIT, comp));
        }
      }
    }
  }

  public void afterComponentDetached(Component comp, Page prevpage) {
    removeBindings(comp);
  }

  public void afterComponentMoved(Component parent, Component child, Component prevparent) {
    // do nothing
  }

  public void afterPageAttached(Page page, Desktop desktop) {
    // do nothing
  }

  public void afterPageDetached(Page page, Desktop prevdesktop) {
    final Collection<Component> comps = page.getRoots();
    for (final Iterator<Component> it = comps.iterator(); it.hasNext(); ) {
      final Component comp = it.next();
      removeBindings(comp);
    }
  }

  private void removeBindings(Component comp) {
    removeBindings0(comp);
    for (final Iterator<Component> it = comp.getChildren().iterator(); it.hasNext(); ) {
      final Component kid = it.next();
      if (kid != null) {
        removeBindings(kid); // recursive
      }
    }
  }

  private void removeBindings0(Component comp) {
    // A component with renderer; might need to remove $MODEL$
    final Object installed = comp.removeAttribute(BinderImpl.RENDERER_INSTALLED);
    if (installed != null) {
      BindELContext.removeModel(comp);
    }
    final Binder binder = BinderUtil.getBinder(comp);
    if (binder != null) {
      binder.removeBindings(comp);
    }

    getExtension().removeLifeCycleHandling(comp);
  }

  private static Extension getExtension() {
    if (_ext == null) {
      synchronized (BindUiLifeCycle.class) {
        if (_ext == null) {
          String clsnm = Library.getProperty("org.zkoss.bind.tracker.impl.extension");
          if (clsnm != null) {
            try {
              _ext = (Extension) Classes.newInstanceByThread(clsnm);
            } catch (Throwable ex) {
              log.realCauseBriefly("Unable to instantiate " + clsnm, ex);
            }
          }
          if (_ext == null) _ext = new DefaultExtension();
        }
      }
    }
    return _ext;
  }
  /**
   * Internal use only. Mark a component and it's children are handling already in current
   * execution. So, if the component attach to component tree(cause {@code
   * #afterComponentAttached(Component, Page)}, BindUiLifeCycle will not process it again.
   */
  public static void markLifeCycleHandling(Component comp) {
    getExtension().markLifeCycleHandling(comp);
  }

  /**
   * An interface used to extend the {@code BindUiLifeCycle}. The class name of the extension shall
   * be specified in the library properties called org.zkoss.bind.tracker.impl.extension.
   *
   * <p>Notice that it is used only internally.
   *
   * @since 6.5.3
   */
  public static interface Extension {
    public void markLifeCycleHandling(Component comp);

    public boolean isLifeCycleHandling(Component comp);

    public void removeLifeCycleHandling(Component comp);
  }

  private static class DefaultExtension implements Extension {
    public void markLifeCycleHandling(Component comp) {}

    public boolean isLifeCycleHandling(Component comp) {
      return false;
    }

    public void removeLifeCycleHandling(Component comp) {}
  }
}
Esempio n. 13
0
/**
 * Utilities related to the Object class.
 *
 * @author tomyeh
 */
public class Objects {
  private static final Log log = Log.lookup(Objects.class);

  /** Denotes unknown. It is useful if both null and unknown is required. */
  public static final Object UNKNOWN = new Object() { // anonymous class
        public final String toString() {
          return "(null)"; // a little different from null
        }
      };

  /** The zero long. */
  public static final Long ZERO_LONG = new Long(0L);
  /** The zero integer. */
  public static final Integer ZERO_INTEGER = new Integer(0);
  /** The zero short. */
  public static final Short ZERO_SHORT = new Short((short) 0);
  /** The zero integer. */
  public static final Byte ZERO_BYTE = new Byte((byte) 0);
  /** The zero float. */
  public static final Float ZERO_FLOAT = new Float(0F);
  /** The zero double. */
  public static final Double ZERO_DOUBLE = new Double(0D);
  /**
   * Represents 0 in big decimal. The same as {@link org.zkoss.math.BigDecimals#ZERO}.
   *
   * @see org.zkoss.math.BigDecimals#ONE
   */
  public static final BigDecimal ZERO_BIG_DECIMAL = new BigDecimal(0D);
  /** Represents 0 in big integer. Same as {@link org.zkoss.math.BigIntegers#ZERO}. */
  public static final BigInteger ZERO_BIG_INTEGER = BigInteger.ZERO;
  /** The null character. */
  public static final Character NULL_CHARACTER = new Character('\u0000');

  /** A separator char that is from the Unicode reserved area. */
  public static final char SEPARATOR_CHAR = 0xf77a; // DON'T CHANGE IT (Or, db broke)
  /** The second separator char that is from the Unicode reserved area. */
  public static final char SEPARATOR_CHAR2 =
      (char) (SEPARATOR_CHAR - 1); // DON'T CHANGE IT (Or, db broke)
  /** A separator char that is from the Unicode reserved area. */
  public static final String SEPARATOR_STRING = "" + SEPARATOR_CHAR;

  /** The path seperator character that is used to construct a path. See org.zkoss.i3.ds.Path. */
  public static final char PATH_SEPARATOR_CHAR = '/';
  /** The path seperator string that is used to construct a path. See org.zkoss.i3.ds.Path. */
  public static final String PATH_SEPARATOR_STRING = "" + PATH_SEPARATOR_CHAR;

  /** The horizontal bar 0 (i.e., .........). */
  public static final String BAR0_STRING = "..\t\t..............................";
  /** The horizontal bar 1 (i.e., ---------). */
  public static final String BAR1_STRING = "--\t\t------------------------------";
  /** The horizontal bar 2 (i.e., =========). */
  public static final String BAR2_STRING = "==\t\t==============================";

  /**
   * Returns the next hash value by giving the previous one and a new one. The caller usually uses a
   * loop to accumulate all related fields.
   *
   * @param prevHashVal the previous hash value returned by this method; 0 if it is the first call.
   * @param newVal the new value to put in
   * @return the new hash value
   */
  public static final int nextHashCode(int prevHashVal, int newVal) {
    return prevHashVal * 31 + newVal;
  }

  /**
   * Generates hash codes for an array of boolean. It is suggested to cache the hash code.
   *
   * @param v the array
   * @return the hash code
   */
  public static final int hashCode(boolean[] v) {
    int h = 1; // not to return 0 if possible, so caller cache it easily
    for (int j = v.length; --j >= 0; ) h = nextHashCode(h, v[j] ? 1 : 0);
    return h;
  }
  /**
   * Generates hash codes for an array of bytes. It is suggested to cache the hash code.
   *
   * @param v the array
   * @return the hash code
   */
  public static final int hashCode(byte[] v) {
    int h = 1; // not to return 0 if possible, so caller cache it easily
    for (int j = v.length; --j >= 0; ) h = nextHashCode(h, v[j]);
    return h;
  }
  /**
   * Generates hash codes for an array of bytes up to the specified length. It is suggested to cache
   * the hash code.
   *
   * @param v the array
   * @param len the maximal length to generate hashCode
   * @return the hash code
   */
  public static final int hashCode(byte[] v, int len) {
    int h = 1; // not to return 0 if possible, so caller cache it easily
    if (len > v.length) len = v.length;
    for (int j = len; --j >= 0; ) h = nextHashCode(h, v[j]);
    return h;
  }
  /**
   * Generates hash codes for an array. It is suggested to cache the hash code.
   *
   * @param v the array
   * @return the hash code
   */
  public static final int hashCode(char[] v) {
    int h = 1; // not to return 0 if possible, so caller cache it easily
    for (int j = v.length; --j >= 0; ) h = nextHashCode(h, v[j]);
    return h;
  }
  /**
   * Generates hash codes for an array. It is suggested to cache the hash code.
   *
   * @param v the array
   * @return the hash code
   */
  public static final int hashCode(short[] v) {
    int h = 1; // not to return 0 if possible, so caller cache it easily
    for (int j = v.length; --j >= 0; ) h = nextHashCode(h, v[j]);
    return h;
  }
  /**
   * Generates hash codes for an array. It is suggested to cache the hash code.
   *
   * @param v the byte array
   * @return the hash code
   */
  public static final int hashCode(int[] v) {
    int h = 1; // not to return 0 if possible, so caller cache it easily
    for (int j = v.length; --j >= 0; ) h = nextHashCode(h, v[j]);
    return h;
  }
  /**
   * Generates hash codes for an array. It is suggested to cache the hash code.
   *
   * @param v the array
   * @return the hash code
   */
  public static final int hashCode(long[] v) {
    int h = 1; // not to return 0 if possible, so caller cache it easily
    for (int j = v.length; --j >= 0; ) {
      h = nextHashCode(h, (int) v[j]);
      h = nextHashCode(h, (int) (v[j] >> 32));
    }
    return h;
  }
  /** Returns the object's hash code, or zero if null. */
  public static final int hashCode(Object o) {
    return o == null ? 0 : o.hashCode();
  }
  /**
   * Tests whether two objects are equals.
   *
   * <p>It takes care of the null case. Thus, it is helpful to implement Object.equals.
   *
   * <p>Notice: it uses compareTo if BigDecimal is found. So, in this case, a.equals(b) might not be
   * the same as Objects.equals(a, b).
   *
   * <p>If both a and b are Object[], they are compared item-by-item.
   */
  public static final boolean equals(Object a, Object b) {
    if (a == b || (a != null && b != null && a.equals(b))) return true;
    if ((a instanceof BigDecimal) && (b instanceof BigDecimal))
      return ((BigDecimal) a).compareTo((BigDecimal) b) == 0;

    if (a == null || !a.getClass().isArray()) return false;

    if ((a instanceof Object[]) && (b instanceof Object[])) {
      final Object[] as = (Object[]) a;
      final Object[] bs = (Object[]) b;
      if (as.length != bs.length) return false;
      for (int j = as.length; --j >= 0; )
        if (!equals(as[j], bs[j])) // recursive
        return false;
      return true;
    }
    if ((a instanceof int[]) && (b instanceof int[])) {
      final int[] as = (int[]) a;
      final int[] bs = (int[]) b;
      if (as.length != bs.length) return false;
      for (int j = as.length; --j >= 0; ) if (as[j] != bs[j]) return false;
      return true;
    }
    if ((a instanceof byte[]) && (b instanceof byte[])) {
      final byte[] as = (byte[]) a;
      final byte[] bs = (byte[]) b;
      if (as.length != bs.length) return false;
      for (int j = as.length; --j >= 0; ) if (as[j] != bs[j]) return false;
      return true;
    }
    if ((a instanceof char[]) && (b instanceof char[])) {
      final char[] as = (char[]) a;
      final char[] bs = (char[]) b;
      if (as.length != bs.length) return false;
      for (int j = as.length; --j >= 0; ) if (as[j] != bs[j]) return false;
      return true;
    }
    if ((a instanceof long[]) && (b instanceof long[])) {
      final long[] as = (long[]) a;
      final long[] bs = (long[]) b;
      if (as.length != bs.length) return false;
      for (int j = as.length; --j >= 0; ) if (as[j] != bs[j]) return false;
      return true;
    }
    if ((a instanceof short[]) && (b instanceof short[])) {
      final short[] as = (short[]) a;
      final short[] bs = (short[]) b;
      if (as.length != bs.length) return false;
      for (int j = as.length; --j >= 0; ) if (as[j] != bs[j]) return false;
      return true;
    }
    if ((a instanceof double[]) && (b instanceof double[])) {
      final double[] as = (double[]) a;
      final double[] bs = (double[]) b;
      if (as.length != bs.length) return false;
      for (int j = as.length; --j >= 0; ) if (as[j] != bs[j]) return false;
      return true;
    }
    if ((a instanceof float[]) && (b instanceof float[])) {
      final float[] as = (float[]) a;
      final float[] bs = (float[]) b;
      if (as.length != bs.length) return false;
      for (int j = as.length; --j >= 0; ) if (as[j] != bs[j]) return false;
      return true;
    }
    if ((a instanceof boolean[]) && (b instanceof boolean[])) {
      final boolean[] as = (boolean[]) a;
      final boolean[] bs = (boolean[]) b;
      if (as.length != bs.length) return false;
      for (int j = as.length; --j >= 0; ) if (as[j] != bs[j]) return false;
      return true;
    }
    return false;
  }

  /**
   * Converts any object to a character array.
   *
   * @param o the object to convert
   * @return the char array or null if o is null
   */
  public static final char[] toCharArray(Object o) {
    if (o == null) return null;
    if (o instanceof char[]) return (char[]) o;

    if (o instanceof String) return ((String) o).toCharArray();

    try {
      Method m = o.getClass().getMethod("toCharArray");
      return (char[]) m.invoke(o);
    } catch (Exception ex) {
      return (o.toString()).toCharArray();
    }
  }
  /**
   * Converts any object to a string. If o is an object array, it invokes {@link ArraysX#toString}
   * to make the string more readable.
   */
  public static final String toString(Object o) {
    if (o == null) return null;
    if (o instanceof Date) return DateFormats.format((Date) o, false);
    if (o instanceof Class) {
      final Class cls = (Class) o;
      final String clsnm = cls.getName();
      if (!clsnm.startsWith("$Proxy")) return "class " + clsnm;

      final Class[] ifs = cls.getInterfaces();
      switch (ifs.length) {
        case 0:
          return "class " + clsnm;
        case 1:
          return "proxy " + Objects.toString(ifs[0]);
        default:
          return "proxy " + Objects.toString(ifs);
      }
    }
    if (o.getClass().isArray()) {
      if (o instanceof Object[]) return ArraysX.toString((Object[]) o);
      if (o instanceof int[]) return ArraysX.toString((int[]) o);
      if (o instanceof short[]) return ArraysX.toString((short[]) o);
      if (o instanceof long[]) return ArraysX.toString((long[]) o);
      if (o instanceof double[]) return ArraysX.toString((double[]) o);
      if (o instanceof byte[]) return ArraysX.toString((byte[]) o);
      if (o instanceof boolean[]) return ArraysX.toString((boolean[]) o);
      if (o instanceof char[]) return ArraysX.toString((char[]) o);
      if (o instanceof float[]) return ArraysX.toString((float[]) o);
    }
    return o.toString();
  }

  /** Converts an integer to a big-endian byte array. */
  public static final byte[] toByteArray(int v) {
    return new byte[] {(byte) (v >>> 24), (byte) (v >>> 16), (byte) (v >>> 8), (byte) v};
  }
  /** Converts a long to a big-endian byte array. */
  public static final byte[] toByteArray(long v) {
    return new byte[] {
      (byte) (v >>> 56), (byte) (v >>> 48), (byte) (v >>> 40), (byte) (v >>> 32),
      (byte) (v >>> 24), (byte) (v >>> 16), (byte) (v >>> 8), (byte) v
    };
  }
  /** Converts a short to a big-endian byte array. */
  public static final byte[] toByteArray(short v) {
    return new byte[] {(byte) (v >>> 8), (byte) v};
  }
  /** Converts a byte to a big-endian byte array. */
  public static final byte[] toByteArray(byte v) {
    return new byte[] {v};
  }

  /**
   * Clones the specified object. Use clone() if Cloeable. Otherwise, try to serialize/deserialize
   * it by use of MarshalledObject.
   *
   * <p>If o is null, null is returned.
   *
   * @exception SystemException if failed to clone
   */
  public static final Object clone(Object o) {
    if (o == null) return o;

    try {
      final Class<?> kls = o.getClass();
      if (kls.isArray()) return ArraysX.clone(o);

      if (o instanceof Cloneable) {
        try {
          return kls.getMethod("clone").invoke(o);
        } catch (NoSuchMethodException ex) {
          if (log.debugable()) log.debug("No clone() for " + kls);
        }
      }

      // :TODO: MarshalledObject is said with very bad performance, change it
      // if exists other good deep clone method.
      return new MarshalledObject<Object>(o).get();
    } catch (Exception ex) {
      throw SystemException.Aide.wrap(ex);
    }
  }
}
public class EmployeeController extends GenericForwardComposer {

  /** */
  private static final long serialVersionUID = -9089818718875587898L;

  private static final Log log = Log.lookup(EmployeeController.class);

  private Employee _currentEmployee;
  private ListModelList _model = EmployeeService.INSTANCE.getModel();

  Listbox lstEmployee;
  Textbox txtFirstName, txtLastName;
  Intbox intAge;

  public Employee getCurrentEmployee() {
    return _currentEmployee;
  }

  public void setCurrentEmployee(Employee currentEmployee) {
    this._currentEmployee = currentEmployee;
  }

  public ListModel getEmployeeModel() {
    return _model;
  }

  // click events
  public void onClick$btnAddEmployee(ForwardEvent fe) {

    String firstName = txtFirstName.getText();
    String lastName = txtLastName.getText();
    int iAge = Integer.parseInt(intAge.getText());

    Employee employee = new Employee(firstName, lastName, iAge);

    if (!_model.add(employee)) {
      reportError(Messages.getString("EmployeeController.0"), employee);
    }
  }

  public void onClick$btnUpdateEmployee(ForwardEvent fe) {
    if (lstEmployee.getSelectedItem() != null) {

      Employee employee = (Employee) (lstEmployee.getSelectedItem().getValue());

      employee.setFirstName(txtFirstName.getText());
      employee.setLastName(txtLastName.getText());
      employee.setAge(Integer.parseInt(intAge.getText()));
    } else {
      UiUtils.showMessage(Messages.getString("EmployeeController.2"));
    }
  }

  public void onClick$btnDeleteEmployee(ForwardEvent fe) {

    if (lstEmployee.getSelectedItem() != null) {
      Employee employee = (Employee) (lstEmployee.getSelectedItem().getValue());

      if (!_model.remove(employee)) {
        reportError(Messages.getString("EmployeeController.3"), employee);
      }

    } else {
      UiUtils.showMessage(Messages.getString("EmployeeController.4"));
    }
  }

  private void reportError(String message, Employee employee) {
    StringBuilder sb =
        new StringBuilder(message)
            .append(Messages.getString("EmployeeController.5"))
            .append(employee);
    final String error = sb.toString();

    UiUtils.showMessage(error);
    log.error(error);
  }
}
Esempio n. 15
0
/**
 * Utilities relevant to content types.
 *
 * @author tomyeh
 */
public class ContentTypes {
  private static final Log log = Log.lookup(ContentTypes.class);

  /** A map of (String format, String contentType). */
  private static final Map<String, String> _fmt2ct = new HashMap<String, String>(64);
  /** A map of (String contentType, String format). */
  private static final Map<String, String> _ct2fmt = new HashMap<String, String>(64);

  protected ContentTypes() {} // prevent from initializing

  /**
   * Returns whether the content type is binary.
   *
   * @param ctype the content type, e.g., text/plain If ctype is null or empty, false is returned.
   * @since 3.0.6
   */
  public static final boolean isBinary(String ctype) {
    return ctype != null
        && ctype.length() > 0
        && !ctype.startsWith("text/")
        && ctype.indexOf("script") < 0;
  }

  /**
   * Returns the content type of the specified format, such as "html" and "pdf", or null if not
   * found (or format is null).
   */
  public static final String getContentType(String format) {
    if (format == null) return null;

    format = format.trim().toLowerCase(java.util.Locale.ENGLISH);
    for (; ; ) {
      synchronized (_fmt2ct) {
        String fmt2ct = _fmt2ct.get(format);
        if (fmt2ct != null) return fmt2ct;
      }

      int j = format.indexOf('.');
      if (j < 0) return null;
      format = format.substring(j + 1);
    }
  }
  /**
   * Returns the format of the specified content type, or null if not found.
   *
   * @exception IllealArgumentException if ctype is null
   */
  public static final String getFormat(String ctype) {
    if (ctype == null) throw new IllegalArgumentException();

    ctype = ctype.trim().toLowerCase(java.util.Locale.ENGLISH);
    String format;
    synchronized (_ct2fmt) {
      format = _ct2fmt.get(ctype);
    }
    if (format == null) {
      // sometime, content type is "text/html;charset=UTF-8"
      int j = ctype.indexOf(';');
      if (j >= 0) {
        ctype = ctype.substring(0, j);
        synchronized (_ct2fmt) {
          format = _ct2fmt.get(ctype);
        }
      }
      if (format == null) {
        j = ctype.indexOf('/');
        format = j >= 0 ? ctype.substring(j + 1) : ctype;
      }
    }
    return format;
  }
  /**
   * Adds additional binding of the format and content type.
   *
   * <p>You rarely need to invoke this method, unless your format is not by the default mapping.
   */
  public static final void put(String format, String ctype) {
    if (format == null || ctype == null) throw new NullPointerException("format or ctype");

    synchronized (_fmt2ct) {
      _fmt2ct.put(format, ctype);
    }
    synchronized (_ct2fmt) {
      _ct2fmt.put(ctype, format);
    }
  }

  static {
    final String flnm = "/metainfo/org/zkoss/util/media/contentTypes.properties";
    if (!load(flnm)) log.warning(MCommon.FILE_NOT_FOUND, flnm);
    load("/contentTypes.properties"); // override!
  }

  private static final boolean load(String flnm) {
    final InputStream strm = ContentTypes.class.getResourceAsStream(flnm);
    if (strm == null) return false;

    BufferedReader in = null;
    // NOTE: we cannot use Properties.load because there might be replicated
    // mapping (e.g., jpg=images/jpg, jpg=images/jpeg)
    try {
      in = new BufferedReader(new InputStreamReader(strm));
      String line;
      while ((line = in.readLine()) != null) {
        final int j = line.indexOf('=');
        if (j < 0) {
          final int k = Strings.skipWhitespaces(line, 0);
          if (k < line.length() && line.charAt(k) != '#')
            log.warning("Ignored error;  illgal format: " + line);
          continue;
        }

        final String format = line.substring(0, j).trim();
        final String ctype = line.substring(j + 1).trim();
        if (format.length() == 0 || ctype.length() == 0) {
          log.warning("Ignored error;  illgal format: " + line);
          continue;
        }

        _fmt2ct.put(format, ctype);
        _ct2fmt.put(ctype, format);
      }
    } catch (IOException ex) {
      log.warning("Ingored error: Unable to read " + flnm, ex);
    } finally {
      if (in != null) {
        try {
          in.close();
        } catch (IOException e) {
        }
      }
      try {
        strm.close();
      } catch (Throwable ex) {
      }
    }
    return true;
  }
}
/**
 * Contains common importing behavior for both XLSX and XLS. Spreadsheet {@link SBook} model
 * including following information: Book: name Sheet: name, (default) column width, (default) row
 * height, hidden row (column), row (column) style, freeze, merge, protection, named range ,
 * gridline display Cell: type, value, font with color and style, type offset(normal or subscript),
 * background color, border's type and color , data format, alignment, wrap, locked, fill pattern
 *
 * <p>We use XLSX, XLS common interface (e.g. CellStyle instead of {@link XSSFCellStyle}) to get
 * content first for that codes can be easily moved to parent class.
 *
 * @author Hawk
 * @since 3.5.0
 */
public abstract class AbstractExcelImporter extends AbstractImporter {
  private static final Log _logger = Log.lookup(AbstractExcelImporter.class);

  /**
   * <poi CellStyle index, {@link SCellStyle} object> Keep track of imported style during importing
   * to avoid creating duplicated style objects.
   */
  protected Map<CellStyle, SCellStyle> importedStyle =
      new HashMap<CellStyle, SCellStyle>(); // ZSS-685
  /** <poi Font index, {@link SFont} object> * */
  protected Map<Short, SFont> importedFont = new HashMap<Short, SFont>();
  /** target book model */
  protected SBook book;
  /** source POI book */
  protected Workbook workbook;
  // ZSS-735, poiPictureData -> SPictureData index
  protected Map<PictureData, Integer> importedPictureData = new HashMap<PictureData, Integer>();

  /** book type key for book attribute * */
  protected static String BOOK_TYPE_KEY = "$ZSS.BOOKTYPE$";

  // ZSS-854
  protected void importDefaultCellStyles() {
    ((AbstractBookAdv) book).clearDefaultCellStyles();
    for (CellStyle poiStyle : workbook.getDefaultCellStyles()) {
      book.addDefaultCellStyle(importCellStyle(poiStyle, false));
    }
    // in case of XLS files which we have not support defaultCellStyles
    if (book.getDefaultCellStyles().isEmpty()) {
      ((AbstractBookAdv) book).initDefaultCellStyles();
    }
    // ZSS-1132: setup default font
    ((AbstractBookAdv) book).initDefaultFont();
  }
  // ZSS-854
  protected void importNamedStyles() {
    ((AbstractBookAdv) book).clearNamedStyles();
    for (NamedStyle poiStyle : workbook.getNamedStyles()) {
      SNamedStyle namedStyle =
          new NamedStyleImpl(
              poiStyle.getName(),
              poiStyle.isCustomBuiltin(),
              poiStyle.getBuiltinId(),
              book,
              poiStyle.getIndex());
      book.addNamedCellstyle(namedStyle);
    }
  }

  /**
   * Import the model according to reversed dependency order among model objects: book, sheet,
   * defined name, cells, chart, pictures, validation.
   */
  @Override
  public SBook imports(InputStream is, String bookName) throws IOException {

    // clear cache for reuse
    importedStyle.clear();
    importedFont.clear();

    workbook = createPoiBook(is);
    book = SBooks.createBook(bookName);
    //		book.setDefaultCellStyle(importCellStyle(workbook.getCellStyleAt((short) 0), false));
    // //ZSS-780
    // ZSS-854
    importDefaultCellStyles();
    importNamedStyles();
    // ZSS-1140
    importExtraStyles();
    setBookType(book);

    // ZSS-715: Enforce internal Locale.US Locale so formula is in consistent internal format
    Locale old = Locales.setThreadLocal(Locale.US);
    SBookSeries bookSeries = book.getBookSeries();
    boolean isCacheClean = bookSeries.isAutoFormulaCacheClean();
    try {
      bookSeries.setAutoFormulaCacheClean(false); // disable it to avoid
      // unnecessary clean up
      // during importing

      importExternalBookLinks();
      int numberOfSheet = workbook.getNumberOfSheets();
      for (int i = 0; i < numberOfSheet; i++) {
        Sheet poiSheet = workbook.getSheetAt(i);
        importSheet(poiSheet, i);
        SSheet sheet = book.getSheet(i);
        importTables(poiSheet, sheet); // ZSS-855, ZSS-1011
      }
      importNamedRange();
      for (int i = 0; i < numberOfSheet; i++) {
        SSheet sheet = book.getSheet(i);
        Sheet poiSheet = workbook.getSheetAt(i);
        for (Row poiRow : poiSheet) {
          importRow(poiRow, sheet);
        }
        importColumn(poiSheet, sheet);
        importMergedRegions(poiSheet, sheet);
        importDrawings(poiSheet, sheet);
        importValidation(poiSheet, sheet);
        importAutoFilter(poiSheet, sheet);
        importSheetProtection(poiSheet, sheet); // ZSS-576
      }
    } finally {
      book.getBookSeries().setAutoFormulaCacheClean(isCacheClean);
      Locales.setThreadLocal(old);
    }

    return book;
  }

  protected abstract Workbook createPoiBook(InputStream is) throws IOException;

  protected abstract void setBookType(SBook book);
  /**
   * Gets the book-type information ("xls" or "xlsx"), return null if not found
   *
   * @param book
   * @return
   */
  public static String getBookType(SBook book) {
    return (String) book.getAttribute(BOOK_TYPE_KEY);
  }

  /**
   * When a column is hidden with default width, we don't import the width for it's 0. We also don't
   * import the width that equals to default width for optimization.
   *
   * @param poiSheet
   * @param sheet
   */
  protected abstract void importColumn(Sheet poiSheet, SSheet sheet);

  /**
   * If in same column: anchorWidthInFirstColumn + anchor width in inter-columns +
   * anchorWidthInLastColumn (dx2) no in same column: anchorWidthInLastColumn - offsetInFirstColumn
   * (dx1)
   */
  protected abstract int getAnchorWidthInPx(ClientAnchor anchor, Sheet poiSheet);

  protected abstract int getAnchorHeightInPx(ClientAnchor anchor, Sheet poiSheet);

  /**
   * Name should be created after sheets created. A special defined name, _xlnm._FilterDatabase
   * (xlsx) or _FilterDatabase (xls), stores the selected cells for auto-filter
   */
  protected void importNamedRange() {
    for (int i = 0; i < workbook.getNumberOfNames(); i++) {
      Name definedName = workbook.getNameAt(i);
      if (skipName(definedName)) {
        continue;
      }
      SName namedRange = null;
      if (definedName.getSheetIndex() == -1) { // workbook scope
        namedRange = book.createName(definedName.getNameName());
      } else {
        namedRange = book.createName(definedName.getNameName(), definedName.getSheetName());
      }
      namedRange.setRefersToFormula(definedName.getRefersToFormula());
    }
  }

  protected boolean skipName(Name definedName) {
    String namename = definedName.getNameName();
    if (namename == null) {
      return true;
    }
    // ignore defined name of functions, they are macro functions that we don't support
    if (definedName.isFunctionName()) {
      return true;
    }

    if (definedName.getRefersToFormula()
        == null) { // ignore defined name with null formula, don't know when will have this case
      return true;
    }

    return false;
  }

  /**
   * Excel uses external book links to map external book index and name. The formula contains full
   * external book name or index only (e.g [book2.xlsx] or [1]). We needs such table for parsing and
   * evaluating formula when necessary.
   */
  protected abstract void importExternalBookLinks();

  // ZSS-952
  protected void importSheetDefaultColumnWidth(Sheet poiSheet, SSheet sheet) {
    // reference XUtils.getDefaultColumnWidthInPx()
    int defaultWidth =
        UnitUtil.defaultColumnWidthToPx(
            poiSheet.getDefaultColumnWidth(), ((AbstractBookAdv) book).getCharWidth()); // ZSS-1132
    sheet.setDefaultColumnWidth(defaultWidth);
  }

  /*
   * import sheet scope content from POI Sheet.
   */
  protected SSheet importSheet(Sheet poiSheet, int poiSheetIndex) {
    SSheet sheet = book.createSheet(poiSheet.getSheetName());
    sheet.setDefaultRowHeight(UnitUtil.twipToPx(poiSheet.getDefaultRowHeight()));
    // ZSS-952
    importSheetDefaultColumnWidth(poiSheet, sheet);
    // reference FreezeInfoLoaderImpl.getRowFreeze()
    sheet.getViewInfo().setNumOfRowFreeze(BookHelper.getRowFreeze(poiSheet));
    sheet.getViewInfo().setNumOfColumnFreeze(BookHelper.getColumnFreeze(poiSheet));
    sheet
        .getViewInfo()
        .setDisplayGridlines(
            poiSheet
                .isDisplayGridlines()); // Note isDisplayGridlines() and isPrintGridlines() are
                                        // different
    sheet.getViewInfo().setColumnBreaks(poiSheet.getColumnBreaks());
    sheet.getViewInfo().setRowBreaks(poiSheet.getRowBreaks());

    SPrintSetup sps = sheet.getPrintSetup();

    SHeader header = sheet.getViewInfo().getHeader();
    if (header != null) {
      header.setCenterText(poiSheet.getHeader().getCenter());
      header.setLeftText(poiSheet.getHeader().getLeft());
      header.setRightText(poiSheet.getHeader().getRight());
      sps.setHeader(header);
    }

    SFooter footer = sheet.getViewInfo().getFooter();
    if (footer != null) {
      footer.setCenterText(poiSheet.getFooter().getCenter());
      footer.setLeftText(poiSheet.getFooter().getLeft());
      footer.setRightText(poiSheet.getFooter().getRight());
      sps.setFooter(footer);
    }

    if (poiSheet.isDiffOddEven()) {
      Header poiEvenHeader = poiSheet.getEvenHeader();
      if (poiEvenHeader != null) {
        SHeader evenHeader = new HeaderFooterImpl();
        evenHeader.setCenterText(poiEvenHeader.getCenter());
        evenHeader.setLeftText(poiEvenHeader.getLeft());
        evenHeader.setRightText(poiEvenHeader.getRight());
        sps.setEvenHeader(evenHeader);
      }
      Footer poiEvenFooter = poiSheet.getEvenFooter();
      if (poiEvenFooter != null) {
        SFooter evenFooter = new HeaderFooterImpl();
        evenFooter.setCenterText(poiEvenFooter.getCenter());
        evenFooter.setLeftText(poiEvenFooter.getLeft());
        evenFooter.setRightText(poiEvenFooter.getRight());
        sps.setEvenFooter(evenFooter);
      }
    }

    if (poiSheet.isDiffFirst()) {
      Header poiFirstHeader = poiSheet.getFirstHeader();
      if (poiFirstHeader != null) {
        SHeader firstHeader = new HeaderFooterImpl();
        firstHeader.setCenterText(poiFirstHeader.getCenter());
        firstHeader.setLeftText(poiFirstHeader.getLeft());
        firstHeader.setRightText(poiFirstHeader.getRight());
        sps.setFirstHeader(firstHeader);
      }
      Footer poiFirstFooter = poiSheet.getFirstFooter();
      if (poiFirstFooter != null) {
        SFooter firstFooter = new HeaderFooterImpl();
        firstFooter.setCenterText(poiFirstFooter.getCenter());
        firstFooter.setLeftText(poiFirstFooter.getLeft());
        firstFooter.setRightText(poiFirstFooter.getRight());
        sps.setFirstFooter(firstFooter);
      }
    }

    PrintSetup poips = poiSheet.getPrintSetup();

    sps.setBottomMargin(poiSheet.getMargin(Sheet.BottomMargin));
    sps.setTopMargin(poiSheet.getMargin(Sheet.TopMargin));
    sps.setLeftMargin(poiSheet.getMargin(Sheet.LeftMargin));
    sps.setRightMargin(poiSheet.getMargin(Sheet.RightMargin));
    sps.setHeaderMargin(poiSheet.getMargin(Sheet.HeaderMargin));
    sps.setFooterMargin(poiSheet.getMargin(Sheet.FooterMargin));

    sps.setAlignWithMargins(poiSheet.isAlignMargins());
    sps.setErrorPrintMode(poips.getErrorsMode());
    sps.setFitHeight(poips.getFitHeight());
    sps.setFitWidth(poips.getFitWidth());
    sps.setHCenter(poiSheet.getHorizontallyCenter());
    sps.setLandscape(poips.getLandscape());
    sps.setLeftToRight(poips.getLeftToRight());
    sps.setPageStart(poips.getUsePage() ? poips.getPageStart() : 0);
    sps.setPaperSize(PoiEnumConversion.toPaperSize(poips.getPaperSize()));
    sps.setCommentsMode(poips.getCommentsMode());
    sps.setPrintGridlines(poiSheet.isPrintGridlines());
    sps.setPrintHeadings(poiSheet.isPrintHeadings());

    sps.setScale(poips.getScale());
    sps.setScaleWithDoc(poiSheet.isScaleWithDoc());
    sps.setDifferentOddEvenPage(poiSheet.isDiffOddEven());
    sps.setDifferentFirstPage(poiSheet.isDiffFirst());
    sps.setVCenter(poiSheet.getVerticallyCenter());

    Workbook poiBook = poiSheet.getWorkbook();
    String area = poiBook.getPrintArea(poiSheetIndex);
    if (area != null) {
      sps.setPrintArea(area);
    }

    CellRangeAddress rowrng = poiSheet.getRepeatingRows();
    if (rowrng != null) {
      sps.setRepeatingRowsTitle(rowrng.getFirstRow(), rowrng.getLastRow());
    }

    CellRangeAddress colrng = poiSheet.getRepeatingColumns();
    if (colrng != null) {
      sps.setRepeatingColumnsTitle(colrng.getFirstColumn(), colrng.getLastColumn());
    }

    sheet.setPassword(poiSheet.getProtect() ? "" : null);

    // import hashed password directly
    importPassword(poiSheet, sheet);

    // ZSS-832
    // import sheet visible
    if (poiBook.isSheetHidden(poiSheetIndex)) {
      sheet.setSheetVisible(SheetVisible.HIDDEN);
    } else if (poiBook.isSheetVeryHidden(poiSheetIndex)) {
      sheet.setSheetVisible(SheetVisible.VERY_HIDDEN);
    } else {
      sheet.setSheetVisible(SheetVisible.VISIBLE);
    }

    // ZSS-1130
    // import conditionalFormatting
    importConditionalFormatting(sheet, poiSheet);
    return sheet;
  }

  protected abstract void importPassword(Sheet poiSheet, SSheet sheet);

  protected void importMergedRegions(Sheet poiSheet, SSheet sheet) {
    // merged cells
    // reference RangeImpl.getMergeAreas()
    int nMerged = poiSheet.getNumMergedRegions();
    final SheetImpl sheetImpl = (SheetImpl) sheet;
    for (int i = nMerged - 1; i >= 0; --i) {
      final CellRangeAddress mergedRegion = poiSheet.getMergedRegion(i);
      // ZSS-1114: any new merged region that overlapped with previous merged region is thrown away
      final CellRegion r =
          new CellRegion(
              mergedRegion.getFirstRow(),
              mergedRegion.getFirstColumn(),
              mergedRegion.getLastRow(),
              mergedRegion.getLastColumn());
      final CellRegion overlapped = sheetImpl.checkMergedRegion(r);
      if (overlapped != null) {
        _logger.warning(
            "Drop the region "
                + r
                + " which is overlapped with existing merged area "
                + overlapped
                + ".");
        continue;
      }
      sheetImpl.addDirectlyMergedRegion(r);
    }
  }

  /** Drawings includes charts and pictures. */
  protected abstract void importDrawings(Sheet poiSheet, SSheet sheet);

  protected abstract void importValidation(Sheet poiSheet, SSheet sheet);

  protected SRow importRow(Row poiRow, SSheet sheet) {
    SRow row = sheet.getRow(poiRow.getRowNum());
    row.setHeight(UnitUtil.twipToPx(poiRow.getHeight()));
    row.setCustomHeight(poiRow.isCustomHeight());
    row.setHidden(poiRow.getZeroHeight());
    CellStyle rowStyle = poiRow.getRowStyle();
    if (rowStyle != null) {
      row.setCellStyle(importCellStyle(rowStyle));
    }

    for (Cell poiCell : poiRow) {
      importCell(poiCell, poiRow.getRowNum(), sheet);
    }

    return row;
  }

  protected SCell importCell(Cell poiCell, int row, SSheet sheet) {

    SCell cell = sheet.getCell(row, poiCell.getColumnIndex());
    cell.setCellStyle(importCellStyle(poiCell.getCellStyle()));

    switch (poiCell.getCellType()) {
      case Cell.CELL_TYPE_NUMERIC:
        cell.setNumberValue(poiCell.getNumericCellValue());
        break;
      case Cell.CELL_TYPE_STRING:
        RichTextString poiRichTextString = poiCell.getRichStringCellValue();
        if (poiRichTextString != null && poiRichTextString.numFormattingRuns() > 0) {
          SRichText richText = cell.setupRichTextValue();
          importRichText(poiCell, poiRichTextString, richText);
        } else {
          cell.setStringValue(poiCell.getStringCellValue());
        }
        break;
      case Cell.CELL_TYPE_BOOLEAN:
        cell.setBooleanValue(poiCell.getBooleanCellValue());
        break;
      case Cell.CELL_TYPE_FORMULA:
        cell.setFormulaValue(poiCell.getCellFormula());
        // ZSS-873
        if (isImportCache() && !poiCell.isCalcOnLoad() && !mustCalc(cell)) {
          ValueEval val = null;
          switch (poiCell.getCachedFormulaResultType()) {
            case Cell.CELL_TYPE_NUMERIC:
              val = new NumberEval(poiCell.getNumericCellValue());
              break;
            case Cell.CELL_TYPE_STRING:
              RichTextString poiRichTextString0 = poiCell.getRichStringCellValue();
              if (poiRichTextString0 != null && poiRichTextString0.numFormattingRuns() > 0) {
                SRichText richText = new RichTextImpl();
                importRichText(poiCell, poiRichTextString0, richText);
                val = new StringEval(richText.getText());
              } else {
                val = new StringEval(poiCell.getStringCellValue());
              }
              break;
            case Cell.CELL_TYPE_BOOLEAN:
              val = BoolEval.valueOf(poiCell.getBooleanCellValue());
              break;
            case Cell.CELL_TYPE_ERROR:
              val = ErrorEval.valueOf(poiCell.getErrorCellValue());
              break;
            case Cell.CELL_TYPE_BLANK:
            default:
              // do nothing
          }
          if (val != null) {
            ((AbstractCellAdv) cell).setFormulaResultValue(val);
          }
        }
        break;
      case Cell.CELL_TYPE_ERROR:
        cell.setErrorValue(PoiEnumConversion.toErrorCode(poiCell.getErrorCellValue()));
        break;
      case Cell.CELL_TYPE_BLANK:
        // do nothing because spreadsheet model auto creates blank cells
      default:
        // TODO log: leave an unknown cell type as a blank cell.
        break;
    }

    Hyperlink poiHyperlink = poiCell.getHyperlink();
    if (poiHyperlink != null) {
      String addr = poiHyperlink.getAddress();
      String label = poiHyperlink.getLabel();
      SHyperlink hyperlink =
          cell.setupHyperlink(
              PoiEnumConversion.toHyperlinkType(poiHyperlink.getType()),
              addr == null ? "" : addr,
              label == null ? "" : label);
      cell.setHyperlink(hyperlink);
    }

    Comment poiComment = poiCell.getCellComment();
    if (poiComment != null) {
      SComment comment = cell.setupComment();
      comment.setAuthor(poiComment.getAuthor());
      comment.setVisible(poiComment.isVisible());
      RichTextString poiRichTextString = poiComment.getString();
      if (poiRichTextString != null && poiRichTextString.numFormattingRuns() > 0) {
        importRichText(poiCell, poiComment.getString(), comment.setupRichText());
      } else {
        comment.setText(poiComment.toString());
      }
    }

    return cell;
  }

  protected void importRichText(
      Cell poiCell, RichTextString poiRichTextString, SRichText richText) {
    String cellValue = poiRichTextString.getString();
    // ZSS-1138
    int count = poiRichTextString.numFormattingRuns();
    if (count <= 0) {
      richText.addSegment(cellValue, null); // ZSS-1138: null font means use cell's font
    } else {
      // ZSS-1138
      int prevFormattingRunIndex = poiRichTextString.getIndexOfFormattingRun(0);
      if (prevFormattingRunIndex > 0) {
        final String content = cellValue.substring(0, prevFormattingRunIndex);
        richText.addSegment(content, null); // ZSS-1138: null font means use cell's font
      }
      for (int i = 0; i < count; i++) {
        int nextFormattingRunIndex =
            (i + 1) >= poiRichTextString.numFormattingRuns()
                ? cellValue.length()
                : poiRichTextString.getIndexOfFormattingRun(i + 1);
        final String content = cellValue.substring(prevFormattingRunIndex, nextFormattingRunIndex);
        richText.addSegment(
            content, toZssFont(getPoiFontFromRichText(workbook, poiCell, poiRichTextString, i)));
        prevFormattingRunIndex = nextFormattingRunIndex;
      }
    }
  }

  /**
   * Convert CellStyle into NCellStyle
   *
   * @param poiCellStyle
   */
  protected SCellStyle importCellStyle(CellStyle poiCellStyle) {
    return importCellStyle(poiCellStyle, true);
  }

  protected SCellStyle importCellStyle(CellStyle poiCellStyle, boolean inStyleTable) {
    SCellStyle cellStyle = null;
    //		short idx = poiCellStyle.getIndex(); // ZSS-685
    if ((cellStyle = importedStyle.get(poiCellStyle)) == null) { // ZSS-685
      cellStyle = book.createCellStyle(inStyleTable);
      importedStyle.put(poiCellStyle, cellStyle); // ZSS-685
      String dataFormat = poiCellStyle.getRawDataFormatString();
      if (dataFormat == null) { // just in case
        dataFormat = SCellStyle.FORMAT_GENERAL;
      }
      if (!poiCellStyle.isBuiltinDataFormat()) {
        cellStyle.setDirectDataFormat(dataFormat);
      } else {
        cellStyle.setDataFormat(dataFormat);
      }
      cellStyle.setWrapText(poiCellStyle.getWrapText());
      cellStyle.setLocked(poiCellStyle.getLocked());
      cellStyle.setAlignment(PoiEnumConversion.toHorizontalAlignment(poiCellStyle.getAlignment()));
      cellStyle.setVerticalAlignment(
          PoiEnumConversion.toVerticalAlignment(poiCellStyle.getVerticalAlignment()));
      cellStyle.setRotation(poiCellStyle.getRotation()); // ZSS-918
      cellStyle.setIndention(poiCellStyle.getIndention()); // ZSS-915
      Color fgColor = poiCellStyle.getFillForegroundColorColor();
      Color bgColor = poiCellStyle.getFillBackgroundColorColor();
      //			if (fgColor == null && bgColor != null) { //ZSS-797
      //				fgColor = bgColor;
      //			}
      // ZSS-857: SOLID pattern: switch fillColor and backColor
      cellStyle.setFillPattern(PoiEnumConversion.toFillPattern(poiCellStyle.getFillPattern()));
      SColor fgSColor = book.createColor(BookHelper.colorToForegroundHTML(workbook, fgColor));
      SColor bgSColor = book.createColor(BookHelper.colorToBackgroundHTML(workbook, bgColor));
      if (cellStyle.getFillPattern() == FillPattern.SOLID) {
        SColor tmp = fgSColor;
        fgSColor = bgSColor;
        bgSColor = tmp;
      }
      cellStyle.setFillColor(fgSColor);
      cellStyle.setBackColor(bgSColor); // ZSS-780

      cellStyle.setBorderLeft(PoiEnumConversion.toBorderType(poiCellStyle.getBorderLeft()));
      cellStyle.setBorderTop(PoiEnumConversion.toBorderType(poiCellStyle.getBorderTop()));
      cellStyle.setBorderRight(PoiEnumConversion.toBorderType(poiCellStyle.getBorderRight()));
      cellStyle.setBorderBottom(PoiEnumConversion.toBorderType(poiCellStyle.getBorderBottom()));

      cellStyle.setBorderLeftColor(
          book.createColor(
              BookHelper.colorToBorderHTML(workbook, poiCellStyle.getLeftBorderColorColor())));
      cellStyle.setBorderTopColor(
          book.createColor(
              BookHelper.colorToBorderHTML(workbook, poiCellStyle.getTopBorderColorColor())));
      cellStyle.setBorderRightColor(
          book.createColor(
              BookHelper.colorToBorderHTML(workbook, poiCellStyle.getRightBorderColorColor())));
      cellStyle.setBorderBottomColor(
          book.createColor(
              BookHelper.colorToBorderHTML(workbook, poiCellStyle.getBottomBorderColorColor())));
      cellStyle.setHidden(poiCellStyle.getHidden());
      // same style always use same font
      cellStyle.setFont(importFont(poiCellStyle));
    }

    return cellStyle;
  }

  protected SFont importFont(CellStyle poiCellStyle) {
    SFont font = null;

    final short fontIndex = poiCellStyle.getFontIndex();
    if (importedFont.containsKey(fontIndex)) {
      font = importedFont.get(fontIndex);
    } else {
      Font poiFont = workbook.getFontAt(fontIndex);
      font = createZssFont(poiFont);
      importedFont.put(fontIndex, font); // ZSS-677
    }
    return font;
  }

  protected SFont toZssFont(Font poiFont) {
    if (poiFont == null) return null; // ZSS-1138

    SFont font = null;
    final short fontIndex = poiFont.getIndex();
    if (importedFont.containsKey(fontIndex)) {
      font = importedFont.get(fontIndex);
    } else {
      font = createZssFont(poiFont);
      importedFont.put(fontIndex, font); // ZSS-677
    }
    return font;
  }

  protected SFont createZssFont(Font poiFont) {
    SFont font = book.createFont(true);
    // font
    font.setName(poiFont.getFontName());
    font.setBoldweight(PoiEnumConversion.toBoldweight(poiFont.getBoldweight()));
    font.setItalic(poiFont.getItalic());
    font.setStrikeout(poiFont.getStrikeout());
    font.setUnderline(PoiEnumConversion.toUnderline(poiFont.getUnderline()));

    font.setHeightPoints(poiFont.getFontHeightInPoints());
    font.setTypeOffset(PoiEnumConversion.toTypeOffset(poiFont.getTypeOffset()));
    font.setColor(book.createColor(BookHelper.getFontHTMLColor(workbook, poiFont)));

    return font;
  }

  protected ViewAnchor toViewAnchor(Sheet poiSheet, ClientAnchor clientAnchor) {
    int width = getAnchorWidthInPx(clientAnchor, poiSheet);
    int height = getAnchorHeightInPx(clientAnchor, poiSheet);
    ViewAnchor viewAnchor =
        new ViewAnchor(clientAnchor.getRow1(), clientAnchor.getCol1(), width, height);
    viewAnchor.setXOffset(getXoffsetInPixel(clientAnchor, poiSheet));
    viewAnchor.setYOffset(getYoffsetInPixel(clientAnchor, poiSheet));
    return viewAnchor;
  }

  protected abstract int getXoffsetInPixel(ClientAnchor clientAnchor, Sheet poiSheet);

  protected abstract int getYoffsetInPixel(ClientAnchor clientAnchor, Sheet poiSheet);

  protected void importPicture(List<Picture> poiPictures, Sheet poiSheet, SSheet sheet) {
    for (Picture poiPicture : poiPictures) {
      PictureData poiPicData = poiPicture.getPictureData();
      Integer picDataIx = importedPictureData.get(poiPicData); // ZSS-735
      if (picDataIx != null) {
        sheet.addPicture(
            picDataIx.intValue(), toViewAnchor(poiSheet, poiPicture.getClientAnchor()));
      } else {
        Format format = Format.valueOfFileExtension(poiPicData.suggestFileExtension());
        if (format != null) {
          SPicture pic =
              sheet.addPicture(
                  format,
                  poiPicData.getData(),
                  toViewAnchor(poiSheet, poiPicture.getClientAnchor()));
          importedPictureData.put(poiPicData, pic.getPictureData().getIndex());
        } else {
          // TODO log we ignore a picture with unsupported format
        }
      }
    }
  }

  /**
   * POI AutoFilter.getFilterColumn(i) sometimes returns null. A POI FilterColumn object only exists
   * when we have set a criteria on that column. For example, if we enable auto filter on 2 columns,
   * but we only set criteria on 2nd column. Thus, the size of filter column is 1. There is only one
   * FilterColumn object and its column id is 1. Only getFilterColumn(1) will return a FilterColumn,
   * other get null.
   *
   * @param poiSheet source POI sheet
   * @param sheet destination sheet
   */
  protected void importAutoFilter(Sheet poiSheet, SSheet sheet) {
    AutoFilter poiAutoFilter = poiSheet.getAutoFilter();
    if (poiAutoFilter != null) {
      CellRangeAddress filteringRange = poiAutoFilter.getRangeAddress();
      SAutoFilter autoFilter =
          sheet.createAutoFilter(new CellRegion(filteringRange.formatAsString()));
      int numberOfColumn = filteringRange.getLastColumn() - filteringRange.getFirstColumn() + 1;
      importAutoFilterColumns(poiAutoFilter, autoFilter, numberOfColumn); // ZSS-1019
    }
  }

  // ZSS-1019
  protected void importAutoFilterColumns(
      AutoFilter poiFilter, SAutoFilter zssFilter, int numberOfColumn) {
    for (int i = 0; i < numberOfColumn; i++) {
      FilterColumn srcColumn = poiFilter.getFilterColumn(i);
      if (srcColumn == null) {
        continue;
      }
      NFilterColumn destColumn = zssFilter.getFilterColumn(i, true);
      destColumn.setProperties(
          PoiEnumConversion.toFilterOperator(srcColumn.getOperator()),
          srcColumn.getCriteria1(),
          srcColumn.getCriteria2(),
          srcColumn.isOn());
    }
  }

  protected org.zkoss.poi.ss.usermodel.Font getPoiFontFromRichText(
      Workbook book, Cell cell, RichTextString rstr, int run) {
    if (run < 0) return null; // ZSS-1138

    org.zkoss.poi.ss.usermodel.Font font =
        rstr instanceof HSSFRichTextString
            ? book.getFontAt(((HSSFRichTextString) rstr).getFontOfFormattingRun(run))
            : ((XSSFRichTextString) rstr).getFontOfFormattingRun((XSSFWorkbook) book, run);
    if (font == null) {
      CellStyle style = cell.getCellStyle();
      short fontIndex = style != null ? style.getFontIndex() : (short) 0;
      return book.getFontAt(fontIndex);
    }
    return font;
  }

  /**
   * POI SheetProtection.
   *
   * @param poiSheet source POI sheet
   * @param sheet destination sheet
   */
  protected abstract void importSheetProtection(Sheet poiSheet, SSheet sheet); // ZSS-576

  /**
   * POI sheet tables
   *
   * @param poiSheet source POI sheet
   * @param sheet destination sheet
   * @since 3.8.0
   */
  protected abstract void importTables(Sheet poiSheet, SSheet sheet); // ZSS-855

  // ZSS-873: Import formula cache result from an Excel file
  protected boolean _importCache = false;
  /**
   * Set if import Excel cached value.
   *
   * @since 3.7.0
   */
  public void setImportCache(boolean b) {
    _importCache = b;
  }
  /**
   * Returns if import file cached value.
   *
   * @since 3.7.0
   */
  protected boolean isImportCache() {
    return _importCache;
  }

  // ZSS-873
  // Must evaluate INDIRECT() function to make the dependency table)
  // Issue845Test-checkIndirectNameRange
  protected boolean mustCalc(SCell cell) {
    FormulaExpression val = ((AbstractCellAdv) cell).getFormulaExpression();
    for (Ptg ptg : val.getPtgs()) {
      if (ptg instanceof FuncVarPtg
          && ((FuncVarPtg) ptg).getFunctionIndex() == 148) { // 148 is INDIRECT
        return true;
      }
    }
    return false;
  }

  // ZSS-1130
  protected abstract void importConditionalFormatting(SSheet sheet, Sheet poiSheet);

  // ZSS-1140
  protected void importExtraStyles() {
    // do nothing; ExcelXlsxImporter should override it
  }
}
Esempio n. 17
0
/**
 * The message manager. This class manages how an message is retrieved based on the message code and
 * the locale.
 *
 * <p>Note: unlike MessageFormat's default behavior, all null objects are treated as an empty string
 * rather than "null".
 *
 * @author tomyeh
 */
public class Messages implements MessageConst {
  private static final Log log = Log.lookup(Messages.class);

  private static Formatter _formatter;

  protected Messages() { // prohibit from inited
  }

  /**
   * Gets a message based on the specified code without formating arugments.
   *
   * <p>Equivalent to get(code, null).
   */
  public static final String get(int code) {
    return get(code, null, getLocale());
  }
  /** Gets a message based on the locale of current user with ONE format-argument. */
  public static final String get(int code, Object fmtArg) {
    return get(code, new Object[] {fmtArg}, getLocale());
  }
  /**
   * Gets a message based on the locale of current user.
   *
   * <p>Equivalent to get(code, fmtArgs, current_locale). The current_locale argument depends on the
   * implementation.
   */
  public static final String get(int code, Object[] fmtArgs) {
    return get(code, fmtArgs, getLocale());
  }

  private static final Locale getLocale() {
    return Locales.getCurrent();
  }

  /**
   * Gets a message from the resource bundle.
   *
   * @return null if no found
   */
  private static final String getFromBundle(int code, Locale locale) {
    final BundleInfo bi = Aide.getBundleInfo(code);
    final PropertyBundle rb = // case insensitive
        PropertyBundle.getBundle(bi.filename, locale, true);
    if (rb != null) return rb.getProperty(Integer.toHexString(code - getType(code)));

    throw new IllegalStateException("Missing resource: " + bi + " locale=" + locale);
  }

  private static final String getNotFound(int code, Locale locale) {
    if (code == NULL_CODE) return ""; // special code

    try {
      log.error(
          "Message code not found: "
              + Integer.toHexString(code)
              + " not in "
              + locale
              + ":"
              + Aide.getBundleInfo(code));

      final String hexcode = Integer.toHexString(code);
      final String s = getFromBundle(MCommon.MESSAGE_CODE_NOT_FOUND, locale);
      return s != null
          ? MessageFormats.format(s, new Object[] {hexcode}, locale)
          : "Unknown message code: " + hexcode;
    } catch (Exception ex) {
      log.realCauseBriefly(ex);
      return "Unknown message code: " + Integer.toHexString(code);
    }
  }

  /**
   * Gets a message based on the specified code. If not found, returns an error message to denote
   * it.
   *
   * <p>If fmtArgs is not null, {@link org.zkoss.text.MessageFormats#format} is called to format the
   * message. However, unlike MessageFormat's default behavior, all null objects are treated as an
   * empty string rather than "null".
   *
   * <p>It also recognizes {@link org.zkoss.lang.Objects#UNKNOWN}.
   *
   * @param code the code
   * @param fmtArgs the argument lists to format the message
   * @param locale the locale of the message to load
   * @return the message; never be null
   */
  public static String get(int code, Object[] fmtArgs, Locale locale) {
    try {
      String s = getFromBundle(code, locale);
      if (s == null) return getNotFound(code, locale);

      if (fmtArgs != null && fmtArgs.length > 0) {
        final Object[] args = new Object[fmtArgs.length];
        final Formatter formatter = _formatter;
        for (int j = 0; j < fmtArgs.length; ++j) {
          final Object arg = fmtArgs[j];
          if (formatter != null) args[j] = formatter.format(arg);
          else if (arg == null || arg == Objects.UNKNOWN) args[j] = "";
          else if (arg instanceof Object[]) args[j] = Objects.toString(arg);
          else args[j] = arg;
        }
        s = MessageFormats.format(s, args, locale);
      }
      return s;
    } catch (Exception ex) {
      log.realCause(ex);
      return getNotFound(code, locale);
    }
  }
  /** Returns the formatter used by {@link #get(int, Object[], Locale)}, or null if not set. */
  public static Formatter getFormatter() {
    return _formatter;
  }
  /**
   * Sets the formatter used by {@link #get(int, Object[], Locale)}.
   *
   * <p>Default: null.
   */
  public static void setFormatter(Formatter fmt) {
    _formatter = fmt;
  }

  /** Gets the message type of the specified code. */
  public static final int getType(int code) {
    return code & 0xffff0000;
  }
  /** The formatter used by {@link #get(int, Object[], Locale)} to format the specified object. */
  public static interface Formatter {
    /** Formats the specified object into a string. */
    public Object format(Object o);
  }
}
Esempio n. 18
0
/**
 * The label loader (implementation only).
 *
 * <p>Used to implement {@link org.zkoss.util.resource.Labels}.
 *
 * <p>Notice that the encoding of the Locale dependent file (*.properties) is assumed to be UTF-8.
 * If it is not the case, please refer to <a
 * href="http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_library-property_Element/Library_Properties#org.zkoss.util.label.web.charset">ZK
 * Configuration Reference</a> for more information.
 *
 * @author tomyeh
 */
public class LabelLoader {
  private static final Log log = Log.lookup(LabelLoader.class);

  /**
   * A map of (Locale l, Map(String key, ExValue label)). We use two maps to speed up the access of
   * labels. _labels allows concurrent access without synchronization. _syncLabels requires
   * synchronization and used for update.
   */
  private Map _labels = Collections.EMPTY_MAP;
  /**
   * A map of (Locale 1, Map<String key1, Map<String key2...> or ExValue label>) It is used by
   * variable resolver and allows ${labels.key1.key2}. _segLabels allows concurrent access without
   * synchronization. See also {@link #getSegmentedLabels}.
   */
  private Map _segLabels = Collections.EMPTY_MAP;
  /** Map<Locale, Map<String, String>>. */
  private final Map _syncLabels = new HashMap(8);
  /** A set of LabelLocator or LabelLocator2. */
  private final Set _locators = new LinkedHashSet(4); // order is important
  /** The XEL context. */
  private final SimpleXelContext _xelc;

  private String _jarcharset, _warcharset;
  private final ExpressionFactory _expf;
  private final FilterMap.Filter _fmfilter;

  public LabelLoader() {
    _fmfilter =
        new FilterMap.Filter() {
          public Object filter(Object key, Object value) {
            return value instanceof ExValue ? ((ExValue) value).getValue() : value;
          }
        };
    _expf = Expressions.newExpressionFactory();
    _xelc = new SimpleXelContext(new Resolver(), null);
  }

  /**
   * Returns the label of the specified key for the current locale, or null if not found.
   *
   * @see #getSegmentedLabels
   */
  public String getLabel(String key) {
    return getLabel(Locales.getCurrent(), key);
  }
  /**
   * Returns the label of the specified key for the specified locale, or null if not found.
   *
   * @since 5.0.7
   */
  public String getLabel(Locale locale, String key) {
    Map map = (Map) _labels.get(locale);
    if (map == null) map = loadLabels(locale);
    final ExValue exVal = (ExValue) map.get(key);
    return exVal != null ? exVal.getValue() : null;
  }
  /**
   * Returns a map of segmented labels for the current locale (never null). Unlike {@link
   * #getLabel}, if a key of the label contains dot, it will be splitted into multiple keys and then
   * grouped into map. It is so-called segmented.
   *
   * <p>For example, the following property file will parsed into a couple of maps, and <code>
   * getSegmentedLabels()</code> returns a map containing a single entry. The entry's key is <code>
   * "a"</code> and the value is another map with two entries <code>"b"</code> and <code>"c"</code>.
   * And, the value for <code>"b"</code> is another two-entries map (containing <code>"c"</code> and
   * <code>"d"</code>).
   *
   * <pre><code>
   * a.b.c=1
   * a.b.d=2
   * a.e=3</pre></code>
   *
   * <p>This method is designed to make labels easier to be accessed in EL expressions.
   *
   * <p>On the other hand, {@link #getLabel} does not split them, and you could access them by, say,
   * <code>getLabel("a.b.d")</code>.
   *
   * @since 5.0.7
   */
  public Map getSegmentedLabels() {
    return getSegmentedLabels(Locales.getCurrent());
  }
  /**
   * Returns a map of segmented labels for the specified locale (never null). Refer to {@link
   * #getSegmentedLabels()} for details.
   *
   * @since 5.0.7
   */
  public Map getSegmentedLabels(Locale locale) {
    final Map map = (Map) _segLabels.get(locale);
    if (map != null) return map;
    loadLabels(locale);
    return (Map) _segLabels.get(locale);
  }

  /**
   * Sets the variable resolver, which is used if an EL expression is specified.
   *
   * @since 3.0.0
   */
  public VariableResolver setVariableResolver(VariableResolver resolv) {
    final Resolver resolver = (Resolver) _xelc.getVariableResolver();
    final VariableResolver old = resolver.custom;
    resolver.custom = resolv;
    return old;
  }
  /**
   * Registers a locator which is used to load the Locale-dependent labels from other resource, such
   * as servlet contexts.
   */
  public void register(LabelLocator locator) {
    register0(locator);
  }
  /**
   * Registers a locator which is used to load the Locale-dependent labels from other resource, such
   * as database.
   *
   * @since 5.0.5
   */
  public void register(LabelLocator2 locator) {
    register0(locator);
  }

  private void register0(Object locator) {
    if (locator == null) throw new NullPointerException("locator");

    synchronized (_locators) {
      if (!_locators.add(locator))
        log.warning("Replace the old one, because it is replicated: " + locator);
    }

    reset(); // Labels might be loaded before, so...
  }
  /**
   * Resets all cached labels and next call to {@link #getLabel} will cause re-loading the
   * Locale-dependent labels.
   */
  public void reset() {
    synchronized (_syncLabels) {
      _syncLabels.clear();
      _segLabels = Collections.EMPTY_MAP;
      _labels = Collections.EMPTY_MAP;
    }
  }

  // -- private utilities --//
  private final Map loadLabels(Locale locale) {
    WaitLock lock = null;
    for (; ; ) {
      final Object o;
      synchronized (_syncLabels) {
        o = _syncLabels.get(locale);
        if (o == null) _syncLabels.put(locale, lock = new WaitLock()); // lock it
      }

      if (o instanceof Map) return (Map) o;
      if (o == null) break; // go to load the page

      // wait because some one is creating the servlet
      if (!((WaitLock) o).waitUntilUnlock(5 * 60 * 1000))
        log.warning(
            "Take too long to wait loading labels: "
                + locale
                + "\nTry to load again automatically...");
    } // for(;;)

    if (_jarcharset == null)
      _jarcharset = Library.getProperty("org.zkoss.util.label.classpath.charset", "UTF-8");
    if (_warcharset == null) {
      _warcharset = Library.getProperty("org.zkoss.util.label.web.charset", null);
      if (_warcharset == null)
        _warcharset =
            Library.getProperty(
                "org.zkoss.util.label.WEB-INF.charset", "UTF-8"); // backward compatible
    }

    try {
      // get the class name
      if (locale != null) log.info("Loading labels for " + locale);
      Map labels = new HashMap(512);

      // 1. load from modules
      final ClassLocator locator = new ClassLocator();
      for (Enumeration en =
              locator.getResources(
                  locale == null
                      ? "metainfo/i3-label.properties"
                      : "metainfo/i3-label_" + locale + ".properties");
          en.hasMoreElements(); ) {
        final URL url = (URL) en.nextElement();
        load(labels, url, _jarcharset);
      }

      // 2. load from extra resource
      final List locators;
      synchronized (_locators) {
        locators = new LinkedList(_locators);
      }
      for (Iterator it = locators.iterator(); it.hasNext(); ) {
        Object o = it.next();
        if (o instanceof LabelLocator) {
          final URL url = ((LabelLocator) o).locate(locale);
          if (url != null) load(labels, url, _warcharset);
        } else {
          final LabelLocator2 loc = (LabelLocator2) o;
          final InputStream is = loc.locate(locale);
          if (is != null) {
            final String cs = loc.getCharset();
            load(labels, is, cs != null ? cs : _warcharset);
          }
        }
      }

      // Convert values to ExValue
      toExValue(labels);

      // merge with labels from 'super' locale
      if (locale != null) {
        final String lang = locale.getLanguage();
        final String cnty = locale.getCountry();
        final String var = locale.getVariant();
        final Map superlabels =
            loadLabels(
                var != null && var.length() > 0
                    ? new Locale(lang, cnty)
                    : cnty != null && cnty.length() > 0 ? new Locale(lang, "") : null);
        if (labels.isEmpty()) {
          labels = superlabels.isEmpty() ? Collections.EMPTY_MAP : superlabels;
        } else if (!superlabels.isEmpty()) {
          Map combined = new HashMap(superlabels);
          combined.putAll(labels);
          labels = combined;
        }
      }

      // add to map
      synchronized (_syncLabels) {
        _syncLabels.put(locale, labels);
        cloneLables();
      }
      return labels;
    } catch (Throwable ex) {
      synchronized (_syncLabels) {
        _syncLabels.remove(locale);
        cloneLables();
      }
      throw SystemException.Aide.wrap(ex);
    } finally {
      lock.unlock(); // unlock (always unlock to avoid deadlock)
    }
  }

  private void toExValue(Map labels) {
    if (!labels.isEmpty())
      for (Iterator it = labels.entrySet().iterator(); it.hasNext(); ) {
        final Map.Entry me = (Map.Entry) it.next();
        me.setValue(new ExValue((String) me.getValue()));
      }
  }
  // Copy _syncLabels to _labels. It must be called in synchronized(_syncLabels)
  private void cloneLables() {
    final Map labels = new HashMap(), segLabels = new HashMap();
    for (Iterator it = _syncLabels.entrySet().iterator(); it.hasNext(); ) {
      final Map.Entry me = (Map.Entry) it.next();
      final Object value = me.getValue();
      if (value instanceof Map) {
        final Object key = me.getKey();
        labels.put(key, value);
        segLabels.put(key, segment((Map) value));
      }
    }
    _labels = labels;
    _segLabels = segLabels;
  }

  private Map segment(Map map) {
    for (Iterator it = map.keySet().iterator(); it.hasNext(); ) {
      final String key = (String) it.next();
      if (key.indexOf(".") >= 0)
        return segmentInner(new HashMap(map)); // clone since we'll modify it
    }
    return new FilterMap(map, _fmfilter); // no special key
  }

  private FilterMap segmentInner(Map map) {
    final Map segFound = new HashMap();
    for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) {
      final Map.Entry me = (Map.Entry) it.next();
      final String key = (String) me.getKey();
      final Object val = me.getValue();
      final int index = key.indexOf('.');
      if (index >= 0) {
        it.remove(); // remove it

        final String newkey = key.substring(0, index);
        Map vals = (Map) segFound.get(newkey);
        if (vals == null) segFound.put(newkey, vals = new HashMap());
        vals.put(key.substring(index + 1), val);
      }
    }

    for (Iterator it = segFound.entrySet().iterator(); it.hasNext(); ) {
      final Map.Entry me = (Map.Entry) it.next();
      final FilterMap seged;
      Object o = map.put(me.getKey(), seged = segmentInner((Map) me.getValue()));
      if (o != null && !(o instanceof Map) /*just in case*/) {
        final Map m = seged.getOrigin();
        o = m.put("$", o);
        if (o != null) m.put("$", o); // restore
      }
    }
    return new FilterMap(map, _fmfilter);
  }

  /** Loads all labels from the specified URL. */
  private static final void load(Map labels, URL url, String charset) throws IOException {
    log.info("Opening " + url); // don't use MCommon since Messages might call getLabel
    load(labels, url.openStream(), charset);
  }
  /** Loads all labels from the specified URL. */
  private static final void load(Map labels, InputStream is, String charset) throws IOException {
    final Map news = new HashMap();
    try {
      Maps.load(news, is, charset);
    } finally {
      try {
        is.close();
      } catch (Throwable ex) {
      }
    }
    for (Iterator it = news.entrySet().iterator(); it.hasNext(); ) {
      final Map.Entry me = (Map.Entry) it.next();
      labels.put(me.getKey(), me.getValue());
    }
  }

  private class ExValue {
    private Expression _expr;
    private String _val;

    public ExValue(String val) {
      int j;
      if ((j = val.indexOf("${")) >= 0 && val.indexOf('}', j + 2) >= 0) {
        try {
          _expr = _expf.parseExpression(_xelc, val, String.class);
          return;
        } catch (Throwable ex) {
          log.error("Illegal expression: " + val, ex);
        }
      }
      _expr = null;
      _val = val;
    }

    public String getValue() {
      return _expr != null ? (String) _expr.evaluate(_xelc) : _val;
    }
  }

  private class Resolver implements VariableResolver, java.io.Serializable {
    private VariableResolver custom;

    public Object resolveVariable(String name) {
      if (custom != null) {
        final Object o =
            custom instanceof VariableResolverX
                ? ((VariableResolverX) custom).resolveVariable(null, null, name)
                : custom.resolveVariable(name);
        if (o != null) return o;
      }
      return getSegmentedLabels().get(name);
    }
  }
}
Esempio n. 19
0
/**
 * Listener to init and cleanup the hibernate session automatically, implement the Hibernate's "Open
 * Session In View" pattern without JTA support. This listener is used with {@link HibernateUtil},
 * or it will not work.
 *
 * <p>In WEB-INF/zk.xml, add following lines:
 *
 * <pre><code>
 * 	&lt;listener>
 * 	&lt;description>Hibernate "OpenSessionInView" Listener&lt;/description>
 * 	&lt;listener-class>org.zkoss.zkplus.hibernate.OpenSessionInViewListener&lt;/listener-class>
 * &lt;/listener>
 * </code></pre>
 *
 * <p>Applicable to Hibernate version 3.2.ga or later
 *
 * @author henrichen
 */
public class OpenSessionInViewListener implements ExecutionInit, ExecutionCleanup {
  private static final Log log = Log.lookup(OpenSessionInViewListener.class);

  // -- ExecutionInit --//
  public void init(Execution exec, Execution parent) {
    if (parent == null) { // the root execution of a servlet request
      log.debug("Starting a database transaction: " + exec);
      HibernateUtil.currentSession().beginTransaction();
    }
  }

  // -- ExecutionCleanup --//
  public void cleanup(Execution exec, Execution parent, List errs) {
    if (parent == null) { // the root execution of a servlet request
      try {
        if (errs == null || errs.isEmpty()) {
          // Commit and cleanup
          log.debug("Committing the database transaction: " + exec);
          HibernateUtil.currentSession().getTransaction().commit();
        } else {
          final Throwable ex = (Throwable) errs.get(0);
          if (ex instanceof StaleObjectStateException) {
            // default implementation does not do any optimistic concurrency
            // control; it simply rollback the transaction.
            handleStaleObjectStateException(exec, (StaleObjectStateException) ex);
          } else {
            // default implementation log the stacktrace and then rollback
            // the transaction.
            handleOtherException(exec, ex);
          }
        }
      } finally {
        HibernateUtil.closeSession(); // always close it
      }
    }
  }

  /**
   * Default StaleObjectStateException handler. This implementation does not implement optimistic
   * concurrency control! It simply rollback the transaction.
   *
   * <p>Application developer might want to extends this class and override this method to do other
   * things like compensate for any permanent changes during the conversation, and finally restart
   * business conversation. Or maybe give the user of the application a chance to merge some of his
   * work with fresh data... what can be done here depends on the applications design.
   *
   * @param exec the exection to clean up.
   * @param ex the StaleObjectStateException being thrown (and not handled) during the execution
   */
  protected void handleStaleObjectStateException(Execution exec, StaleObjectStateException ex) {
    log.error("This listener does not implement optimistic concurrency control!");
    rollback(exec, ex);
  }

  /**
   * Default other exception (other than StaleObjectStateException) handler. This implementation
   * simply rollback the transaction.
   *
   * <p>Application developer might want to extends this class and override this method to do other
   * things like compensate for any permanent changes during the conversation, and finally restart
   * business conversation... what can be done here depends on the applications design.
   *
   * @param exec the exection to clean up.
   * @param ex the Throwable other than StaleObjectStateException being thrown (and not handled)
   *     during the execution
   */
  protected void handleOtherException(Execution exec, Throwable ex) {
    // Rollback only
    ex.printStackTrace();
    rollback(exec, ex);
  }

  /**
   * rollback the current session.
   *
   * @param exec the exection to clean up.
   * @param ex the StaleObjectStateException being thrown (and not handled) during the execution
   */
  private void rollback(Execution exec, Throwable ex) {
    try {
      if (HibernateUtil.currentSession().getTransaction().isActive()) {
        log.debug("Trying to rollback database transaction after exception:" + ex);
        HibernateUtil.currentSession().getTransaction().rollback();
      }
    } catch (Throwable rbEx) {
      log.error("Could not rollback transaction after exception! Original Exception:\n" + ex, rbEx);
    }
  }
}
Esempio n. 20
0
/**
 * Utilities to handle java.io.Serializable.
 *
 * @author tomyeh
 */
public class Serializables {
  /**
   * The logger called org.zkoss.io.serializable used to log serialization information. You could
   * set it to DEBUG, such that the not-serializable and ignored values will be logged.
   *
   * @since 5.0.7
   */
  public static final Log logio = Log.lookup("org.zkoss.io.serializable");

  private Serializables() {}

  /**
   * Writes only serializable entries of the specified map. Non-serializable attributes are ignored.
   */
  public static <K, V> void smartWrite(ObjectOutputStream s, Map<K, V> map) throws IOException {
    if (map != null) {
      final boolean debug = logio.debugable();
      for (Map.Entry<K, V> me : map.entrySet()) {
        final K nm = me.getKey();
        final V val = me.getValue();
        if (((nm instanceof Serializable) || (nm instanceof Externalizable))
            && (val == null || (val instanceof Serializable) || (val instanceof Externalizable))) {
          try {
            s.writeObject(nm);
            s.writeObject(val);
          } catch (java.io.NotSerializableException ex) {
            logio.error("Unable to serialize entry: " + nm + '=' + val);
            throw ex;
          }
        } else if (nm != null && debug) {
          logio.debug("Skip not-serializable entry: " + nm + '=' + val);
        }
      }
    }
    s.writeObject(null); // denote end-of-map
  }
  /**
   * Reads serializable entries back (serialized by {@link #smartWrite(ObjectOutputStream,Map)}).
   *
   * @param map the map to hold the data being read. If null and any data is read, a new map
   *     (HashMap) is created and returned.
   * @return the map being read
   */
  @SuppressWarnings("unchecked")
  public static <K, V> Map<K, V> smartRead(ObjectInputStream s, Map<K, V> map)
      throws IOException, ClassNotFoundException {
    for (; ; ) {
      final Object nm = s.readObject();
      if (nm == null) break; // no more

      if (map == null) map = new HashMap<K, V>();
      map.put((K) nm, (V) s.readObject());
    }
    return map;
  }
  /** Writes only serializable elements of the specified collection. */
  public static <T> void smartWrite(ObjectOutputStream s, Collection<T> col) throws IOException {
    if (col != null) {
      final boolean debug = logio.debugable();
      for (T val : col) {
        if ((val instanceof Serializable) || (val instanceof Externalizable)) {
          try {
            s.writeObject(val);
          } catch (java.io.NotSerializableException ex) {
            logio.error("Unable to serialize item: " + val);
            throw ex;
          }
        } else if (val != null && debug) {
          logio.debug("Skip not-serializable item: " + val);
        }
      }
    }
    s.writeObject(null);
  }
  /**
   * Reads serializable elements back (serialized by {@link
   * #smartWrite(ObjectOutputStream,Collection)})
   *
   * @param col the collection to hold the data beinig read. If null and and data is read, a new
   *     collection (LinkedList) is created and returned.
   * @return the collection being read
   */
  @SuppressWarnings("unchecked")
  public static <T> Collection<T> smartRead(ObjectInputStream s, Collection<T> col)
      throws IOException, ClassNotFoundException {
    for (; ; ) {
      final Object val = s.readObject();
      if (val == null) break; // no more

      if (col == null) col = new LinkedList<T>();
      col.add((T) val);
    }
    return col;
  }
  /**
   * Reads serializable elements back (serialized by {@link
   * #smartWrite(ObjectOutputStream,Collection)})
   *
   * @param col the collection to hold the data beinig read. If null and and data is read, a new
   *     collection (LinkedList) is created and returned.
   * @return the collection being read
   * @since 6.0.0
   */
  @SuppressWarnings("unchecked")
  public static <T> List<T> smartRead(ObjectInputStream s, List<T> col)
      throws IOException, ClassNotFoundException {
    for (; ; ) {
      final Object val = s.readObject();
      if (val == null) break; // no more

      if (col == null) col = new LinkedList<T>();
      col.add((T) val);
    }
    return col;
  }

  /**
   * Writes only serializable elements of the specified array.
   *
   * <p>To read back, use {@link #smartRead(ObjectInputStream, Collection)}.
   *
   * @since 3.0.0
   */
  public static <T> void smartWrite(ObjectOutputStream s, T[] ary) throws IOException {
    if (ary != null) {
      final boolean debug = logio.debugable();
      for (int j = 0; j < ary.length; ++j) {
        final T val = ary[j];
        if ((val instanceof Serializable) || (val instanceof Externalizable)) {
          try {
            s.writeObject(val);
          } catch (java.io.NotSerializableException ex) {
            logio.error("Unable to serialize item: " + val);
            throw ex;
          }
        } else if (val != null && debug) {
          logio.debug("Skip not-serializable item: " + val);
        }
      }
    }
    s.writeObject(null);
  }
  /**
   * Writes the given value only if it is serializable. If not, null is written.
   *
   * @since 5.0.7
   */
  public static void smartWrite(ObjectOutputStream s, Object val) throws IOException {
    final boolean bser =
        val instanceof java.io.Serializable || val instanceof java.io.Externalizable;
    s.writeObject(bser ? val : null);
    if (!bser && val != null && logio.debugable())
      logio.debug("Skip not-serializable object: " + val);
  }
}