/* * Copyright 2011 cruxframework.org. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.cruxframework.crux.core.declarativeui; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.cruxframework.crux.core.client.screen.LazyPanelWrappingType; import org.cruxframework.crux.core.client.screen.views.ViewFactoryUtils; import org.cruxframework.crux.core.rebind.screen.ViewFactory; import org.cruxframework.crux.core.rebind.screen.widget.WidgetLibraries; import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagChild; import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagChildLazyCondition; import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagChildLazyConditions; import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagChildren; import org.cruxframework.crux.core.utils.HTMLUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.google.gwt.core.ext.typeinfo.NotFoundException; /** * @author Thiago da Rosa de Bustamante * */ public class LazyWidgets { /** * Checkers for widgets that lazily create its children */ private static Map<String, WidgetLazyChecker> lazyWidgetCheckers = new HashMap<String, WidgetLazyChecker>(); /** * Lazy checker for invisible panels */ private WidgetLazyChecker defaultLazyChecker = new WidgetLazyChecker() { public boolean isLazy(JSONObject widget) { return widget.has("visible") && !widget.optBoolean("visible"); } }; private final boolean escapeXML; public LazyWidgets(boolean escapeXML) { this.escapeXML = escapeXML; } /** * @param childrenMethod * @param factoryHelper * @param declaredCheckers * @throws NotFoundException */ private static void initializeLazyChecker(Class<?> factoryType, List<WidgetLazyChecker> declaredCheckers, Set<String> added) { String className = factoryType.getCanonicalName(); if (!added.contains(className)) { added.add(className); TagChildren tagChildren = factoryType.getAnnotation(TagChildren.class); if (tagChildren != null) { for (TagChild child : tagChildren.value()) { Class<?> childProcessor = child.value(); TagChildLazyConditions lazyConditions = childProcessor.getAnnotation(TagChildLazyConditions.class); if (lazyConditions != null) { WidgetLazyChecker lazyChecker = initializeLazyChecker(lazyConditions); if (lazyChecker != null) { declaredCheckers.add(lazyChecker); } } initializeLazyChecker(childProcessor, declaredCheckers, added); } } } } /** * @param type * @throws ClassNotFoundException * @throws NotFoundException */ private static void initializeLazyChecker(String type) throws ClassNotFoundException { String widgetFactoryClass = WidgetLibraries.getInstance().getFactoryClass(type); Class<?> factoryType = Class.forName(widgetFactoryClass); final List<WidgetLazyChecker> declaredCheckers = new ArrayList<WidgetLazyChecker>(); initializeLazyChecker(factoryType, declaredCheckers, new HashSet<String>()); if (declaredCheckers.size() == 0) { lazyWidgetCheckers.put(type, null); } else if (declaredCheckers.size() == 1) { lazyWidgetCheckers.put(type, declaredCheckers.get(0)); } else { lazyWidgetCheckers.put(type, new WidgetLazyChecker() { public boolean isLazy(JSONObject widget) { boolean ret = false; for (WidgetLazyChecker widgetLazyChecker : declaredCheckers) { ret = ret || widgetLazyChecker.isLazy(widget); } return ret; } }); } } /** * @param lazyConditions * @return */ public static WidgetLazyChecker initializeLazyChecker(final TagChildLazyConditions lazyConditions) { if (lazyConditions.all().length > 0) { return new WidgetLazyChecker() { public boolean isLazy(JSONObject widget) { boolean lazy = true; for (TagChildLazyCondition lazyCondition : lazyConditions.all()) { String property = lazyCondition.property(); if (lazyCondition.equals().length() > 0) { lazy = lazy && (widget.has(property) && widget.optString(property).equals(lazyCondition.equals())); } else if (lazyCondition.notEquals().length() > 0) { lazy = lazy && (!widget.has(property) || !widget.optString(property).equals(lazyCondition.notEquals())); } if (!lazy) { break; } } return lazy; } }; } else if (lazyConditions.any().length > 0) { return new WidgetLazyChecker() { public boolean isLazy(JSONObject widget) { boolean lazy = false; for (TagChildLazyCondition lazyCondition : lazyConditions.any()) { String property = lazyCondition.property(); if (lazyCondition.equals().length() > 0) { lazy = lazy || (widget.has(property) && widget.optString(property).equals(lazyCondition.equals())); } else if (lazyCondition.notEquals().length() > 0) { lazy = lazy || (!widget.has(property) || !widget.optString(property).equals(lazyCondition.notEquals())); } if (lazy) { break; } } return lazy; } }; } return null; } public String generateScreenLazyDeps(String cruxMetadataArray) throws LazyExeption { try { JSONArray meta = new JSONArray(cruxMetadataArray); JSONObject dependencies = new JSONObject(); int length = meta.length(); for (int i = 0; i < length; i++) { JSONObject compCandidate = meta.getJSONObject(i); generateScreenLazyDeps(dependencies, compCandidate); } return dependencies.toString(); } catch (Exception e) { throw new LazyExeption(e.getMessage(), e); } } /** * Return true if the parent widget informed, must render its children lazily. * * @param parent * @return * @throws JSONException * @throws ClassNotFoundException */ private boolean checkChildrenLazy(JSONObject parent) throws JSONException, ClassNotFoundException { String parentType = parent.getString("_type"); if (!lazyWidgetCheckers.containsKey(parentType)) { initializeLazyChecker(parentType); } WidgetLazyChecker checker = lazyWidgetCheckers.get(parentType); return checker != null && checker.isLazy(parent); } /** * @param dependencies * @param widget * @throws JSONException * @throws ClassNotFoundException */ private void checkChildrenLazyDeps(JSONObject dependencies, JSONObject widget) throws JSONException, ClassNotFoundException { if (widget.has("_children")) { JSONArray children = widget.getJSONArray("_children"); int length = children.length(); for (int i = 0; i < length; i++) { JSONObject child = children.getJSONObject(i); generateScreenLazyDeps(dependencies, child); } } } /** * Return true if the widget informed, must be rendered lazily. * * @param widget * @return * @throws JSONException */ private boolean checkLazy(JSONObject widget) throws JSONException { return defaultLazyChecker.isLazy(widget); } /** * Search the widget's children adding lazy dependencies between all children and the parent informed. * * @param widget * @param parentId * @param dependencies * @throws JSONException * @throws ClassNotFoundException */ private void generateLazyDepsForChildren(JSONObject widget, String parentId, JSONObject dependencies) throws JSONException, ClassNotFoundException { if (widget.has("_children")) { JSONArray children = widget.getJSONArray("_children"); int size = children.length(); for (int i=0; i<size; i++) { JSONObject child = children.getJSONObject(i); if (child != null) { String lazyId = parentId; if (ViewFactory.isValidWidget(child)) { String childId = child.getString("id"); addDependency(dependencies, childId, parentId); if (checkLazy(child)) { lazyId = ViewFactoryUtils.getLazyPanelId(childId, LazyPanelWrappingType.wrapWholeWidget); } else if (checkChildrenLazy(child)) { lazyId = ViewFactoryUtils.getLazyPanelId(childId, LazyPanelWrappingType.wrapChildren); } } generateLazyDepsForChildren(child, lazyId, dependencies); } } } } /** * @param dependencies * @param widget * @throws JSONException * @throws ClassNotFoundException */ private void generateScreenLazyDeps(JSONObject dependencies, JSONObject widget) throws JSONException, ClassNotFoundException { if (ViewFactory.isValidWidget(widget)) { boolean wholeWidgetLazy = checkLazy(widget); boolean widgetChildrenLazy = checkChildrenLazy(widget); if (wholeWidgetLazy || widgetChildrenLazy) { String widgetId = widget.getString("id"); String wrapperId = null; if (wholeWidgetLazy) { wrapperId = ViewFactoryUtils.getLazyPanelId(widgetId, LazyPanelWrappingType.wrapWholeWidget); addDependency(dependencies, widgetId, wrapperId); } /* * if both 'wholeWidgetLazy' and 'widgetChildrenLazy' are true, all widgets children must be dependent * of a wrapper lazyPanel created for the children and not be dependent of the a wrapper created over its parent. */ if (widgetChildrenLazy) { wrapperId = ViewFactoryUtils.getLazyPanelId(widgetId, LazyPanelWrappingType.wrapChildren); } generateLazyDepsForChildren(widget, wrapperId, dependencies); } else { checkChildrenLazyDeps(dependencies, widget); } } else { checkChildrenLazyDeps(dependencies, widget); } } /** * @param dependencies * @param widgetId * @param wrapperId * @return * @throws JSONException */ private JSONObject addDependency(JSONObject dependencies, String widgetId, String wrapperId) throws JSONException { return dependencies.put(HTMLUtils.escapeJavascriptString(widgetId, escapeXML), HTMLUtils.escapeJavascriptString(wrapperId, escapeXML)); } /** * @author Thiago da Rosa de Bustamante * */ public static interface WidgetLazyChecker { boolean isLazy(JSONObject widget); } }