org.zkoss.zk.grails.composer.GrailsComposer.java Source code

Java tutorial

Introduction

Here is the source code for org.zkoss.zk.grails.composer.GrailsComposer.java

Source

/* GrailsComposer.java
    
Copyright (C) 2008-2011 Chanwit Kaewkasi
    
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package org.zkoss.zk.grails.composer;

import groovy.lang.Closure;
import groovy.lang.MetaClass;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import java.util.*;

import javax.servlet.http.HttpServletRequest;

import org.codehaus.groovy.grails.commons.GrailsApplication;
import org.codehaus.groovy.grails.commons.GrailsClass;
import org.codehaus.groovy.grails.commons.GrailsClassUtils;

import org.codehaus.groovy.runtime.InvokerHelper;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;

import org.zkoss.util.Pair;
import org.zkoss.zk.grails.DesktopCounter;
import org.zkoss.zk.grails.GrailsComet;
import org.zkoss.zk.grails.MessageHolder;
import org.zkoss.zk.grails.scaffolding.ScaffoldingTemplate;
import org.zkoss.zk.grails.select.JQuery;
import org.zkoss.zk.grails.select.JQueryExtender;
import org.zkoss.zk.grails.ZkBuilder;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.event.BookmarkEvent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.ForwardEvent;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.metainfo.ComponentInfo;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zk.ui.sys.ComponentsCtrl;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zkplus.spring.SpringUtil;

import org.zkoss.zk.grails.route.RouteEngine;

public class GrailsComposer extends GenericForwardComposer<Component> {

    private static final long serialVersionUID = -5307023773234300419L;
    private MessageHolder messageHolder = null;

    // inject
    private DesktopCounter desktopCounter;
    // component holder for Selector
    private Component root;

    public Component getRoot() {
        return root;
    }

    public void setRoot(Component root) {
        this.root = root;
    }

    public GrailsComposer() {
        //default is true
        super('_', true, true);
    }

    public void setDesktopCounter(DesktopCounter dc) {
        this.desktopCounter = dc;
    }

    public DesktopCounter getDesktopCounter() {
        return this.desktopCounter;
    }

    public void activateDesktop() throws java.lang.InterruptedException {
        desktopCounter.activate(this.desktop);
    }

    public void deactivateDesktop() throws java.lang.InterruptedException {
        desktopCounter.deactivate(this.desktop);
    }

    public void enablePush() {
        desktopCounter.enablePush(this.desktop);
    }

    public void disablePush() {
        desktopCounter.disablePush(this.desktop);
    }

    public Desktop getDesktop() {
        return this.desktop;
    }

    public Page getPage() {
        return this.page;
    }

    public ZkBuilder getBuild() {
        ZkBuilder builder = new ZkBuilder();
        builder.setPage(page);
        return builder;
    }

    public MessageHolder getMessage() {
        if (messageHolder == null) {
            HttpServletRequest request = (HttpServletRequest) (this.desktop.getExecution().getNativeRequest());
            messageHolder = new MessageHolder(page);
        }
        return messageHolder;
    }

    public String message(String code) {
        return getMessage().getAt(code);
    }

    public String message(Map<?, ?> map) {
        return getMessage().call(map);
    }

    public void injectComet() throws Exception {
        Field[] fields = this.getClass().getDeclaredFields();
        for (Field f : fields) {
            if (f.getName().endsWith("Comet")) {
                GrailsComet gc = (GrailsComet) InvokerHelper.getProperty(this, f.getName());
                gc.setGrailsComposer(this);
            }
        }
    }

    private Map params;

    public Map getParams() {
        return this.params;
    }

    @Override
    public void doAfterCompose(Component comp) throws Exception {
        try {
            super.doAfterCompose(comp);
            this.root = comp;
            //
            // Issue #328 - check if the desktop is null to prevent NPE
            //
            // if(comp.getDesktop() == null) {
            //     this.params = new HashMap();
            // } else {
            //    this.params = (Map)(comp.getDesktop().removeAttribute("$JQ_REQUEST_PARAMS$"));
            // }
            // this should be working with Issue #328 as well
            Desktop desktop = Executions.getCurrent().getDesktop();
            if (desktop == null) {
                this.params = new HashMap();
            } else {
                this.params = (Map) desktop.removeAttribute("$JQ_REQUEST_PARAMS$");
            }

            if (this.params == null) {
                this.params = new HashMap();
            }

            injectComet();

            handleRoutingClosure(comp);

            // work only on <window/> component
            comp.addEventListener("onBookmarkChange", new org.zkoss.zk.ui.event.EventListener<Event>() {
                public void onEvent(Event event) throws Exception {
                    BookmarkEvent be = (BookmarkEvent) event;
                    String hashTag = be.getBookmark();
                    if (hashTag.startsWith("!")) {
                        hashTag = hashTag.substring(1);
                    }

                    String[] parsedHashTag = hashTag.split("\\/");
                    String[] args = Arrays.copyOfRange(parsedHashTag, 1, parsedHashTag.length);
                    MetaClass mc = InvokerHelper.getMetaClass(GrailsComposer.this);
                    if (mc.respondsTo(GrailsComposer.this, parsedHashTag[0]).size() > 0) {
                        InvokerHelper.invokeMethod(GrailsComposer.this, parsedHashTag[0], args);
                    }

                }
            });

            handleAfterComposeClosure(comp);
            handleScaffold(comp);

            Selectors.wireVariables(comp, this, null);
            Selectors.wireEventListeners(comp, this);

            //
            // See JQuery#redirect()
            //
            // comp.getDesktop().setAttribute("$JQ_REQUESTING_COMPOSER$", GrailsComposer.this);
            if (InvokerHelper.getMetaClass(GrailsComposer.this).respondsTo(GrailsComposer.this, "index")
                    .size() > 0) {
                InvokerHelper.invokeMethod(GrailsComposer.this, "index", new Object[] {});
                // comp.getDesktop().setAttribute("$JQ_REQUESTING_COMPOSER$", null);
            }

        } catch (Exception e) {
            // grails.util.GrailsUtil.printSanitizedStackTrace(e);
            throw e;
        }
    }

    private Map<Pair<Component, String>, List<Method>> selectorBasedHandler = new HashMap<Pair<Component, String>, List<Method>>();

    public List<Method> getSelectorBasedHandler(Pair<Component, String> pair) {
        return selectorBasedHandler.get(pair);
    }

    private static final Method[] EMPTY_METHODS = new Method[] {};

    private Method[] getHandlerMethod(Class<?> cls, Event event) {
        Method method = ComponentsCtrl.getEventMethod(cls, event.getName());
        if (method != null)
            return new Method[] { method };
        List<Method> result = selectorBasedHandler
                .get(new Pair<Component, String>(event.getTarget(), event.getName()));
        if (result == null)
            return EMPTY_METHODS;
        return result.toArray(new Method[result.size()]);
    }

    /**
     * <p>Overrides GenericEventListener to use InvokerHelper to call methods. Because of this the events are now
     * part of groovy's dynamic methods, e.g. metaClass.invokeMethod works for event methods. Without this the default java code
     * don't call the overriden invokeMethod</p>
     *
     * @param event Event object
     * @throws Exception
     */
    @Override
    public void onEvent(Event event) throws Exception {
        try {
            final Object controller = getController();
            final Method[] methods = getHandlerMethod(controller.getClass(), event);
            if (methods.length == 0)
                return;
            for (Method method : methods) {
                if (method != null) {
                    if (method.getParameterTypes().length == 0) {
                        InvokerHelper.invokeMethod(controller, method.getName(), null);
                    } else if (event instanceof ForwardEvent) { //ForwardEvent
                        final Class<?> paramcls = method.getParameterTypes()[0];
                        //paramcls is ForwardEvent || Event
                        if (ForwardEvent.class.isAssignableFrom(paramcls) || Event.class.equals(paramcls)) {
                            InvokerHelper.invokeMethod(controller, method.getName(), new Object[] { event });
                        } else {
                            do {
                                event = ((ForwardEvent) event).getOrigin();
                            } while (event instanceof ForwardEvent);
                            InvokerHelper.invokeMethod(controller, method.getName(), new Object[] { event });
                        }
                    } else {
                        Annotation[][] anns = method.getParameterAnnotations();
                        // one parameter, and annotation-less
                        if (anns.length == 1 && anns[0].length == 0) {
                            InvokerHelper.invokeMethod(controller, method.getName(), new Object[] { event });
                        } /* else {
                          Object[] params = new Object[anns.length];
                          int i = 0;
                          for(Annotation[] paramAnno: anns) {
                              for(Annotation a: paramAnno) {
                                  if(a instanceof Attr) {
                                      String attrName = ((Attr) a).value();
                                      params[i] = ComponentUtil.attr(event.getTarget(), attrName);
                                      break;
                                  }
                              }
                              i++;
                          }
                          InvokerHelper.invokeMethod(controller, method.getName(), params);
                          }*/
                    }
                }
            }
        } catch (Exception e) {
            // grails.util.GrailsUtil.printSanitizedStackTrace(e);
            throw e;
        }
    }

    /*
    private boolean handleBeforeComposeClosure(Page page, Component parent) {
    try {
        Object c = GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(this, "beforeCompose");
        if (c instanceof Closure) {
            Object b = ((Closure)c).call(page, parent);
            if(b instanceof Boolean) {
                return (Boolean)b;
            } else {
                return true;
            }
        }
    } catch (BeansException e) { do nothing }
    return true;
    }*/

    private RouteEngine getRouteEngine() {
        Desktop desktop = Executions.getCurrent().getDesktop();
        RouteEngine routeEngine = (RouteEngine) desktop.getAttribute("$ZK_GRAILS_ROUTE_ENGINE$");
        if (routeEngine == null) {
            routeEngine = new RouteEngine();
            desktop.setAttribute("$ZK_GRAILS_ROUTE_ENGINE$", routeEngine);
        }
        return routeEngine;
    }

    private void handleRoutingClosure(Component comp) throws Exception {
        Object c = GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(this, "routing");
        if (c instanceof Closure) {
            Closure routing = (Closure) c;
            final RouteEngine routeEngine = getRouteEngine();
            routing.setDelegate(routeEngine);
            routing.setResolveStrategy(Closure.DELEGATE_FIRST);
            InvokerHelper.invokeClosure(c, new Object[] { routeEngine });

            Component root = comp.getRoot();
            if (root != null) {
                root.addEventListener("onBookmarkChange", new org.zkoss.zk.ui.event.EventListener<Event>() {
                    public void onEvent(Event event) throws Exception {
                        BookmarkEvent be = (BookmarkEvent) event;
                        String bookmark = be.getBookmark();
                        InvokerHelper.invokeMethod(routeEngine, "process", new Object[] { bookmark });
                    }
                });
            }
        }
    }

    private void handleAfterComposeClosure(Component wnd) throws Exception {
        Object c = GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(this, "afterCompose");
        if (c instanceof Closure) {
            Class c1 = ((Closure) c).getParameterTypes()[0];
            if (c1.isAssignableFrom(wnd.getClass())) {
                InvokerHelper.invokeClosure(c, new Object[] { wnd });
            } else {
                throw new Exception("\nAt " + this.getClass() + "#afterCompose = { | -> }.\n"
                        + "Please change type of the argument\n" + "    from: [" + c1 + "]\n" + "    to:   ["
                        + wnd.getClass() + "].");
            }
        }
    }

    private void handleScaffold(Component comp) {
        try {
            ApplicationContext ctx = SpringUtil.getApplicationContext();

            Object scaffold = GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(this, "scaffold");
            if (scaffold != null) {
                GrailsApplication app = ctx.getBean(GrailsApplication.APPLICATION_ID, GrailsApplication.class);

                ScaffoldingTemplate template = ctx.getBean(ScaffoldingTemplate.SCAFFOLDING_TEMPLATE,
                        ScaffoldingTemplate.class);

                if (scaffold instanceof Boolean) {
                    if (((Boolean) scaffold)) {
                        //
                        // Use this to find class name
                        // and cut "Composer" off.
                        //
                        String name = this.getClass().getName().replaceAll("Composer", "");

                        //
                        // Look for the domain class.
                        //
                        GrailsClass domainClass = app.getArtefact("Domain", name);
                        Class<?> klass = domainClass.getClazz();
                        template.initComponents(klass, comp, app);
                    }
                } else if (scaffold instanceof Map) {
                    //
                    // Example, [template: 'erza', domain: Catalog]
                    //
                    Map smap = ((Map) scaffold);
                    String templateName = (String) smap.get("template");
                    if (templateName == null) {
                        templateName = "zkgrails";
                    }
                    Class<?> klass = (Class<?>) smap.get("domain");
                    if (klass == null) {
                        String name = this.getClass().getName().replaceAll("Composer", "");
                        GrailsClass domainClass = app.getArtefact("Domain", name);
                        klass = domainClass.getClazz();
                    }
                    template = ctx.getBean(templateName + "ScaffoldingTemplate", ScaffoldingTemplate.class);
                    template.initComponents(klass, comp, app);
                } else {
                    template.initComponents((Class<?>) scaffold, comp, app);
                }
            }
        } catch (BeansException e) {
            System.out.println("Warning : " + e.getMessage());
        }
    }

    /**
     * injectable APIs
    **/

    public Object get_() {
        return new JQueryExtender();
    }

    public MetaClass $() {
        return InvokerHelper.getMetaClass(JQuery.class);
    }

    public JQuery $(String arg) {
        return JQuery.select(root, new Object[] { arg });
    }

    public JQuery $(Object[] args) {
        return JQuery.select(root, args);
    }

    public JQuery $d(String arg) {
        Desktop desktop = root.getDesktop();
        Collection<Component> results = new HashSet<Component>();
        for (Page p : desktop.getPages()) {
            for (Component root : p.getRoots()) {
                results.addAll(Selectors.find(root, arg));
            }
        }
        return new JQuery(new ArrayList<Component>(results));
    }

    public void redirect(Map map) {
        String uri = map.get("uri").toString();
        Executions.sendRedirect(uri);
    }

    public void notify(String message) {
        Clients.showNotification(message, true);
    }

    public void notify(String message, Component ref) {
        Clients.showNotification(message, ref);
    }

    public void notify(String message, JQuery selected) {
        Clients.showNotification(message, selected.getComponents().get(0));
    }

}