001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.extensions.markup.html.repeater.tree; 018 019import java.util.List; 020import java.util.Optional; 021import java.util.Set; 022 023import org.apache.wicket.core.request.handler.IPartialPageRequestHandler; 024import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable; 025import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; 026import org.apache.wicket.extensions.markup.html.repeater.tree.table.ITreeColumn; 027import org.apache.wicket.extensions.markup.html.repeater.tree.table.ITreeDataProvider; 028import org.apache.wicket.extensions.markup.html.repeater.tree.table.NodeModel; 029import org.apache.wicket.extensions.markup.html.repeater.tree.table.TreeDataProvider; 030import org.apache.wicket.markup.repeater.IItemReuseStrategy; 031import org.apache.wicket.markup.repeater.Item; 032import org.apache.wicket.markup.repeater.RefreshingView; 033import org.apache.wicket.markup.repeater.data.IDataProvider; 034import org.apache.wicket.model.IModel; 035import org.apache.wicket.util.lang.Args; 036import org.apache.wicket.util.visit.IVisit; 037import org.apache.wicket.util.visit.IVisitor; 038 039/** 040 * A tree with tabular markup. 041 * 042 * @author svenmeier 043 * 044 * @param <T> 045 * The model object type 046 * @param <S> 047 * the type of the sort property 048 */ 049public abstract class TableTree<T, S> extends AbstractTree<T> 050{ 051 private static final long serialVersionUID = 1L; 052 053 private final DataTable<T, S> table; 054 055 /** 056 * Constructor 057 * 058 * @param id 059 * component id 060 * @param columns 061 * list of IColumn objects 062 * @param dataProvider 063 * imodel for data provider 064 * @param rowsPerPage 065 * number of rows per page 066 */ 067 public TableTree(final String id, final List<? extends IColumn<T, S>> columns, 068 final ITreeProvider<T> dataProvider, final long rowsPerPage) 069 { 070 this(id, columns, dataProvider, rowsPerPage, null); 071 } 072 073 /** 074 * Constructor 075 * 076 * @param id 077 * component id 078 * @param columns 079 * list of IColumn objects 080 * @param provider 081 * provider of the tree 082 * @param rowsPerPage 083 * number of rows per page 084 * @param state 085 * the expansion state 086 */ 087 public TableTree(final String id, final List<? extends IColumn<T, S>> columns, 088 final ITreeProvider<T> provider, final long rowsPerPage, IModel<? extends Set<T>> state) 089 { 090 super(id, provider, state); 091 092 Args.notEmpty(columns, "columns"); 093 for (IColumn<T, S> column : columns) 094 { 095 if (column instanceof ITreeColumn<?, ?>) 096 { 097 ((ITreeColumn<T, S>)column).setTree(this); 098 } 099 } 100 101 this.table = newDataTable("table", columns, newDataProvider(provider), rowsPerPage); 102 add(table); 103 104 // see #updateBranch(Object, AjaxRequestTarget) 105 setOutputMarkupId(true); 106 } 107 108 /** 109 * Factory method for the wrapped {@link DataTable}. 110 * 111 * Note: If overwritten, the DataTable's row items have to output their markupId, or 112 * {@link #updateNode(Object, IPartialPageRequestHandler)} will fail. 113 * 114 * @param id 115 * @param columns 116 * @param dataProvider 117 * @param rowsPerPage 118 * @return nested data table 119 */ 120 protected DataTable<T, S> newDataTable(String id, List<? extends IColumn<T, S>> columns, 121 IDataProvider<T> dataProvider, long rowsPerPage) 122 { 123 return new DataTable<T, S>(id, columns, dataProvider, rowsPerPage) 124 { 125 private static final long serialVersionUID = 1L; 126 127 @Override 128 protected Item<T> newRowItem(String id, int index, IModel<T> model) 129 { 130 Item<T> item = TableTree.this.newRowItem(id, index, model); 131 132 // see #update(Node); 133 item.setOutputMarkupId(true); 134 135 return item; 136 } 137 }; 138 } 139 140 /** 141 * Get the nested table. 142 * 143 * @return the nested table 144 */ 145 public DataTable<T, S> getTable() 146 { 147 return table; 148 } 149 150 /** 151 * Sets the item reuse strategy. This strategy controls the creation of {@link Item}s. 152 * 153 * @see RefreshingView#setItemReuseStrategy(IItemReuseStrategy) 154 * @see IItemReuseStrategy 155 * 156 * @param strategy 157 * item reuse strategy 158 * @return this for chaining 159 */ 160 @Override 161 public final TableTree<T, S> setItemReuseStrategy(final IItemReuseStrategy strategy) 162 { 163 table.setItemReuseStrategy(strategy); 164 165 super.setItemReuseStrategy(strategy); 166 167 return this; 168 } 169 170 /** 171 * For updating of a single branch the whole table is added to the ART. 172 */ 173 @Override 174 public void updateBranch(T node, IPartialPageRequestHandler target) 175 { 176 // TableTree always outputs markupId 177 target.add(this); 178 } 179 180 /** 181 * For an update of a node the complete row item is added to the ART. 182 */ 183 @Override 184 public void updateNode(T t, IPartialPageRequestHandler target) 185 { 186 final IModel<T> model = getProvider().model(t); 187 table.getBody().visitChildren(Item.class, new IVisitor<Item<T>, Void>() 188 { 189 @Override 190 public void component(Item<T> item, IVisit<Void> visit) 191 { 192 NodeModel<T> nodeModel = (NodeModel<T>)item.getModel(); 193 194 if (model.equals(nodeModel.getWrappedModel())) 195 { 196 // row items are configured to output their markupId 197 target.add(item); 198 visit.stop(); 199 return; 200 } 201 visit.dontGoDeeper(); 202 } 203 }); 204 model.detach(); 205 } 206 207 /** 208 * Hook method to create an {@link ITreeDataProvider}. 209 * 210 * @param provider 211 * the tree provider 212 * @return the data provider 213 */ 214 protected ITreeDataProvider<T> newDataProvider(ITreeProvider<T> provider) 215 { 216 return new TreeDataProvider<T>(provider) 217 { 218 private static final long serialVersionUID = 1L; 219 220 @Override 221 protected boolean iterateChildren(T object) 222 { 223 return TableTree.this.getState(object) == State.EXPANDED; 224 } 225 }; 226 } 227 228 /** 229 * Create a row item for the nested {@link DataTable}. 230 * 231 * @param id 232 * component id 233 * @param index 234 * index of row 235 * @param model 236 * model for row 237 * @return row item 238 */ 239 protected Item<T> newRowItem(String id, int index, IModel<T> model) 240 { 241 Item<T> item = new Item<>(id, index, model); 242 243 return item; 244 } 245}