Java tutorial
/* * 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 cc.alcina.framework.gwt.client.objecttree; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.gwt.user.client.ui.ComplexPanel; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Widget; import com.totsp.gwittir.client.beans.Binding; import com.totsp.gwittir.client.beans.BindingBuilder; import com.totsp.gwittir.client.ui.AbstractBoundWidget; import com.totsp.gwittir.client.ui.Renderer; import com.totsp.gwittir.client.ui.table.Field; import com.totsp.gwittir.client.ui.util.BoundWidgetTypeFactory; import cc.alcina.framework.common.client.WrappedRuntimeException; import cc.alcina.framework.common.client.logic.permissions.Permissible; import cc.alcina.framework.common.client.logic.permissions.PermissionsManager; import cc.alcina.framework.common.client.provider.TextProvider; import cc.alcina.framework.common.client.search.HasWithNull; import cc.alcina.framework.common.client.util.CommonUtils; import cc.alcina.framework.gwt.client.gwittir.GwittirBridge; import cc.alcina.framework.gwt.client.gwittir.GwittirBridge.BoundWidgetTypeFactorySimpleGenerator; import cc.alcina.framework.gwt.client.gwittir.provider.ListBoxCollectionProvider; import cc.alcina.framework.gwt.client.gwittir.provider.ListBoxEnumProvider; import cc.alcina.framework.gwt.client.logic.RenderContext; import cc.alcina.framework.gwt.client.objecttree.TreeRenderer.RenderInstruction; import cc.alcina.framework.gwt.client.widget.RelativePopupValidationFeedback; /** * * @author Nick Reddel */ public class ObjectTreeRenderer { public static final String SEARCH_SECTIONS = "SEARCH_SECTIONS"; private FlowPanelWithBinding op; protected BoundWidgetTypeFactory factory = new BoundWidgetTypeFactorySimpleGenerator(); protected Map<Widget, TreeRenderer> level1ContentRendererMap = new HashMap<Widget, TreeRenderer>(); protected Map<Widget, TreeRenderer> level1LabelMap = new HashMap<Widget, TreeRenderer>(); public ComplexPanel render(TreeRenderable root) { try { return render(root, RenderContext.branch()); } finally { RenderContext.merge(); } } public ComplexPanel render(TreeRenderable root, RenderContext renderContext) { this.op = new FlowPanelWithBinding(); op.setRenderContext(renderContext); renderToPanel(root, op, 0, true, renderContext, null); op.setRenderContext(renderContext); op.getBinding().bind(); op.getBinding().setLeft(); op.setStyleName("alcina-ObjectTree"); return op; } private void maybeSortChildRenderables(List<? extends TreeRenderable> childRenderables, final RenderContext renderContext) { if (renderContext.get(SEARCH_SECTIONS) != null) { final List<String> sectionOrder = renderContext.get(SEARCH_SECTIONS); Collections.sort(childRenderables, new Comparator<TreeRenderable>() { Map<TreeRenderable, Integer> lkp = new HashMap<TreeRenderable, Integer>(); @Override public int compare(TreeRenderable o1, TreeRenderable o2) { return CommonUtils.compareInts(getIndex(o1), getIndex(o2)); } private int getIndex(TreeRenderable r) { if (!lkp.containsKey(r)) { TreeRenderer node1 = TreeRenderingInfoProvider.get().getForRenderable(r, renderContext); String s1 = node1.section(); lkp.put(r, sectionOrder.indexOf(s1)); } return lkp.get(r); } }); } } @SuppressWarnings("unchecked") protected void renderToPanel(TreeRenderable renderable, ComplexPanel cp, int depth, boolean soleChild, RenderContext renderContext, TreeRenderer parent) { if (renderable instanceof Permissible) { Permissible permissible = (Permissible) renderable; if (!PermissionsManager.get().isPermissible(permissible)) { return; } } TreeRenderer node = TreeRenderingInfoProvider.get().getForRenderable(renderable, renderContext); if (parent != null) { parent.childRenderers().add(node); } else { renderContext.setRootRenderer(node); } if (depth == 0 && node.renderCss() != null) { cp.setStyleName(node.renderCss()); } node.parentBinding(op.binding); boolean widgetsAdded = false; Collection<? extends TreeRenderer> children = node.renderableChildren(); // title AbstractBoundWidget customiserWidget = null; RenderInstruction renderInstruction = node.renderInstruction(); IsRenderableFilter renderableFilter = renderContext.getRenderableFilter(); if (renderableFilter != null && !renderableFilter.isRenderable(renderable, node)) { renderInstruction = RenderInstruction.NO_RENDER; } if (renderInstruction != RenderInstruction.NO_RENDER) { customiserWidget = node.renderCustomiser() == null ? null : (AbstractBoundWidget) node.renderCustomiser().get(); } level1ContentRendererMap.put(customiserWidget, node); switch (renderInstruction) { case NO_RENDER: return; case AS_WIDGET_WITH_TITLE_IF_MORE_THAN_ONE_CHILD: if (customiserWidget == null && soleChild) { break; } case AS_TITLE: case AS_WIDGET: String displayName = renderable.getDisplayName(); if (CommonUtils.isNotNullOrEmpty(displayName) && !node.isNoTitle()) { Label label = TextProvider.get() .getInlineLabel(TextProvider.get().getUiObjectText(node.getClass(), TextProvider.DISPLAY_NAME + "-" + displayName, CommonUtils.upperCaseFirstLetterOnly(displayName) + ": ")); label.setStyleName("level-" + ((soleChild) ? Math.max(1, depth - 1) : depth)); cp.add(label); if (depth == 1) { level1LabelMap.put(label, node); } widgetsAdded = true; } default: break; } if (customiserWidget != null) { // note - must be responsible for own detach - cleanup customiserWidget.setModel(renderable); node.setBoundWidget(customiserWidget); if (node.renderCss() != null) { customiserWidget.addStyleName(node.renderCss()); } String customiserStyleName = node.isSingleLineCustomiser() ? "single-line-customiser" : "customiser"; String title = node.title(); if (title != null) { customiserWidget.setTitle(title); title = null; } String hint = node.hint(); if (hint != null) { FlowPanel fp2 = new FlowPanel(); Label label = new Label(hint); label.setStyleName("hint"); fp2.add(customiserWidget); fp2.add(label); fp2.addStyleName(customiserStyleName); cp.add(fp2); } else { customiserWidget.addStyleName(customiserStyleName); cp.add(customiserWidget); } return; } if (node.renderInstruction() == RenderInstruction.AS_WIDGET_WITH_TITLE_IF_MORE_THAN_ONE_CHILD || node.renderInstruction() == RenderInstruction.AS_WIDGET) { AbstractBoundWidget bw = new ObjectTreeBoundWidgetCreator().createBoundWidget(renderable, depth, soleChild, node, op.getBinding()); node.setBoundWidget(bw); cp.add(bw); widgetsAdded = true; } if (children != null && children.size() != 0) { ComplexPanel childPanel = cp; if (depth != 0) { if (node.renderChildrenHorizontally()) { HorizontalPanel hp = new HorizontalPanel(); hp.setVerticalAlignment(HasVerticalAlignment.ALIGN_BOTTOM); childPanel = hp; } else { childPanel = new FlowPanel(); } } if (childPanel != cp) { level1ContentRendererMap.put(childPanel, node); cp.add(childPanel); } List<? extends TreeRenderable> childRenderables = new ArrayList<TreeRenderable>( node.renderableChildren()); maybeSortChildRenderables(childRenderables, renderContext); for (TreeRenderable child : childRenderables) { renderToPanel(child, childPanel, depth + 1, node.renderableChildren().size() == 1, renderContext, node); } } return; } public static class FlowPanelWithBinding extends FlowPanel implements SupportsAttachDetachCallbacks { private Binding binding = new Binding(); private RenderContext renderContext; public Binding getBinding() { return binding; } public void setBinding(Binding binding) { this.binding = binding; } public void setRenderContext(RenderContext renderContext) { this.renderContext = renderContext; } @Override protected void onAttach() { super.onAttach(); if (renderContext != null) { renderContext.onAttach(this); } } @Override protected void onDetach() { super.onDetach(); if (renderContext != null) { renderContext.onDetach(this); } binding.unbind(); } } public static class HorizontalPanelWithBinding extends HorizontalPanel { private Binding binding = new Binding(); public Binding getBinding() { return binding; } public void setBinding(Binding binding) { this.binding = binding; } @Override protected void onDetach() { super.onDetach(); binding.unbind(); } } public static class ObjectTreeBoundWidgetCreator { public AbstractBoundWidget createBoundWidget(TreeRenderable renderable, int depth, boolean soleChild, TreeRenderer node, Binding parentBinding) { String propertyName = node.renderablePropertyName(); Class type = GwittirBridge.get().getProperty(renderable, propertyName).getType(); Field f = GwittirBridge.get().getField(renderable.getClass(), propertyName, true, false); RelativePopupValidationFeedback vf = new RelativePopupValidationFeedback( RelativePopupValidationFeedback.BOTTOM, f.getFeedback()); vf.addCssBackground(); if (f.getCellProvider() instanceof ListBoxEnumProvider && renderable instanceof HasWithNull) { ((ListBoxEnumProvider) f.getCellProvider()).setWithNull(((HasWithNull) renderable).isWithNull()); } if (f.getCellProvider() instanceof ListBoxCollectionProvider && node.collectionFilter() != null) { ListBoxCollectionProvider lbcp = (ListBoxCollectionProvider) f.getCellProvider(); lbcp.setFilter(node.collectionFilter()); } if (f.getCellProvider() instanceof ListBoxEnumProvider) { Renderer renderer = node.renderer(); if (renderer != null) { ((ListBoxEnumProvider) f.getCellProvider()).setRenderer(renderer); } } AbstractBoundWidget bw = null; try { bw = (AbstractBoundWidget) f.getCellProvider().get(); } catch (Exception e) { throw new WrappedRuntimeException("Exception rendering object tree", e); } Binding binding = BindingBuilder.bind(bw).onLeftProperty("value").validateLeftWith(f.getValidator()) .notifiedWithLeft(f.getFeedback() != null ? vf : null).toRight(renderable) .onRightProperty(propertyName).convertRightWith(f.getConverter()).toBinding(); if (parentBinding != null) { parentBinding.getChildren().add(binding); } if (node.renderCss() != null) { bw.setStyleName(node.renderCss()); } bw.addStyleName("level-" + ((soleChild) ? Math.max(1, depth - 1) : depth) + "-widget"); return bw; } } public interface SupportsAttachDetachCallbacks { public void setRenderContext(RenderContext renderContext); } }