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.markup.repeater; 018 019import java.util.Iterator; 020 021import org.apache.wicket.markup.repeater.util.ModelIteratorAdapter; 022import org.apache.wicket.model.IModel; 023import org.apache.wicket.util.lang.Generics; 024 025 026/** 027 * An abstract repeater view that provides refreshing functionality to its subclasses. The view is 028 * refreshed every request, making it well suited for displaying dynamic data. 029 * <p> 030 * The view is populated by implementing {@link RefreshingView#getItemModels() } and 031 * {@link RefreshingView#populateItem(Item) } methods. RefreshingView builds the items that will be 032 * rendered by looping over the models retrieved from {@link RefreshingView#getItemModels() } and 033 * calling the {@link RefreshingView#newItem(String, int, IModel) } to generate the child item 034 * container followed by a call to {@link RefreshingView#populateItem(Item) } to let the user 035 * populate the newly created item container with custom components. 036 * </p> 037 * <p> 038 * The provided {@link ModelIteratorAdapter} can make implementing 039 * {@link RefreshingView#getItemModels() } easier if you have an iterator over item objects. 040 * </p> 041 * 042 * @see RepeatingView 043 * @see ModelIteratorAdapter 044 * 045 * @author Igor Vaynberg (ivaynberg) 046 * 047 * @param <T> 048 * type of elements contained in the model's list 049 */ 050public abstract class RefreshingView<T> extends RepeatingView 051{ 052 private static final long serialVersionUID = 1L; 053 054 /** 055 * The item reuse strategy that will be used to recycle items when the page is changed or the 056 * view is redrawn. 057 * 058 * @see IItemReuseStrategy 059 */ 060 private IItemReuseStrategy itemReuseStrategy; 061 062 /** 063 * Constructor 064 * 065 * @param id 066 * component id 067 */ 068 public RefreshingView(String id) 069 { 070 super(id); 071 } 072 073 /** 074 * Constructor 075 * 076 * @param id 077 * component id 078 * @param model 079 * model 080 */ 081 public RefreshingView(String id, IModel<?> model) 082 { 083 super(id, model); 084 } 085 086 /** 087 * Refresh the items in the view. Delegates the creation of items to the selected item reuse 088 * strategy 089 */ 090 @Override 091 protected final void onPopulate() 092 { 093 Iterator<IModel<T>> models = getItemModels(); 094 Iterator<Item<T>> items = getItemReuseStrategy().getItems(newItemFactory(), models, 095 getItems()); 096 removeAll(); 097 addItems(items); 098 } 099 100 /** 101 * Create a new IItemFactory based upon the RefreshingView 102 * 103 * @return An Item factory that delegates to the RefreshingView 104 */ 105 protected IItemFactory<T> newItemFactory() 106 { 107 return new IItemFactory<T>() 108 { 109 @Override 110 public Item<T> newItem(int index, IModel<T> model) 111 { 112 String id = RefreshingView.this.newChildId(); 113 Item<T> item = RefreshingView.this.newItem(id, index, model); 114 RefreshingView.this.populateItem(item); 115 return item; 116 } 117 }; 118 } 119 120 121 /** 122 * Returns an iterator over models for items that will be added to this view 123 * 124 * @return an iterator over models for items that will be added to this view 125 */ 126 protected abstract Iterator<IModel<T>> getItemModels(); 127 128 /** 129 * Populate the given Item container. 130 * <p> 131 * <b>be careful</b> to add any components to the item and not the view itself. So, don't do: 132 * 133 * <pre> 134 * add(new Label("foo", "bar")); 135 * </pre> 136 * 137 * but: 138 * 139 * <pre> 140 * item.add(new Label("foo", "bar")); 141 * </pre> 142 * 143 * </p> 144 * 145 * @param item 146 * The item to populate 147 */ 148 protected abstract void populateItem(final Item<T> item); 149 150 /** 151 * Factory method for Item container. Item containers are simple MarkupContainer used to 152 * aggregate the user added components for a row inside the view. 153 * 154 * @see Item 155 * @param id 156 * component id for the new data item 157 * @param index 158 * the index of the new data item 159 * @param model 160 * the model for the new data item 161 * 162 * @return DataItem created DataItem 163 */ 164 protected Item<T> newItem(final String id, int index, final IModel<T> model) 165 { 166 return new Item<>(id, index, model); 167 } 168 169 /** 170 * @return iterator over item instances that exist as children of this view 171 */ 172 public Iterator<Item<T>> getItems() 173 { 174 return Generics.iterator(iterator()); 175 } 176 177 /** 178 * Add items to the view. Prior to this all items were removed so every request this function 179 * starts from a clean slate. 180 * 181 * @param items 182 * item instances to be added to this view 183 */ 184 protected void addItems(Iterator<Item<T>> items) 185 { 186 int index = 0; 187 while (items.hasNext()) 188 { 189 Item<T> item = items.next(); 190 item.setIndex(index); 191 add(item); 192 ++index; 193 } 194 } 195 196 // ///////////////////////////////////////////////////////////////////////// 197 // ITEM GENERATION 198 // ///////////////////////////////////////////////////////////////////////// 199 200 /** 201 * @return currently set item reuse strategy. Defaults to <code>DefaultItemReuseStrategy</code> 202 * if none was set. 203 * 204 * @see DefaultItemReuseStrategy 205 */ 206 public IItemReuseStrategy getItemReuseStrategy() 207 { 208 if (itemReuseStrategy == null) 209 { 210 return DefaultItemReuseStrategy.getInstance(); 211 } 212 return itemReuseStrategy; 213 } 214 215 /** 216 * Sets the item reuse strategy. This strategy controls the creation of {@link Item}s. 217 * 218 * @see IItemReuseStrategy 219 * 220 * @param strategy 221 * item reuse strategy 222 * @return this for chaining 223 */ 224 public RefreshingView<T> setItemReuseStrategy(IItemReuseStrategy strategy) 225 { 226 if (strategy == null) 227 { 228 throw new IllegalArgumentException(); 229 } 230 231 if (!strategy.equals(itemReuseStrategy)) 232 { 233 if (isVersioned()) 234 { 235 addStateChange(); 236 } 237 itemReuseStrategy = strategy; 238 } 239 return this; 240 } 241 242 243}