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.feedback; 018 019import java.io.Serializable; 020import java.util.ArrayList; 021import java.util.List; 022import java.util.Optional; 023 024import org.apache.wicket.Component; 025import org.apache.wicket.MetaDataKey; 026import org.apache.wicket.Page; 027import org.apache.wicket.WicketRuntimeException; 028import org.apache.wicket.request.cycle.RequestCycle; 029 030/** 031 * Postpone calling {@link IFeedback#beforeRender()} after other components. 032 * <p> 033 * This gives other {@link Component#beforeRender()} the possibility to report feedbacks, 034 * which can then be collected by {@link IFeedback}s afterwards. 035 */ 036public class FeedbackDelay implements Serializable, AutoCloseable 037{ 038 private static final MetaDataKey<FeedbackDelay> KEY = new MetaDataKey<>() 039 { 040 private static final long serialVersionUID = 1L; 041 }; 042 043 private List<IFeedback> feedbacks = new ArrayList<>(); 044 045 private RequestCycle cycle; 046 047 /** 048 * Delay all feedbacks for the given cycle. 049 * <p> 050 * All postponed feedbacks will be prepared for render with {@link #beforeRender()}. 051 * 052 * @param cycle 053 * request cycle 054 */ 055 public FeedbackDelay(RequestCycle cycle) { 056 if (get(cycle).isPresent()) { 057 throw new WicketRuntimeException("feedbacks are already delayed"); 058 } 059 060 cycle.setMetaData(KEY, this); 061 062 this.cycle = cycle; 063 } 064 065 /** 066 * Get the current delay. 067 * 068 * @param cycle 069 * @return optional delay 070 */ 071 public static Optional<FeedbackDelay> get(RequestCycle cycle) { 072 return Optional.ofNullable(cycle.getMetaData(KEY)); 073 } 074 075 /** 076 * Postpone {@link Component#beforeRender()} on the given feedback. 077 * 078 * @param feedback 079 * @return 080 */ 081 public FeedbackDelay postpone(IFeedback feedback) { 082 feedbacks.add(feedback); 083 084 return this; 085 } 086 087 /** 088 * Prepares all postponed feedbacks for render. 089 * 090 * @see IFeedback#beforeRender() 091 */ 092 public void beforeRender() { 093 cycle.setMetaData(KEY, null); 094 cycle = null; 095 096 for (IFeedback feedback : feedbacks) 097 { 098 if (feedback instanceof Component) { 099 Component component = (Component)feedback; 100 101 // render only if it is still in the page hierarchy (WICKET-4895) 102 if (component.findParent(Page.class) == null) 103 { 104 continue; 105 } 106 } 107 108 feedback.beforeRender(); 109 } 110 } 111 112 /** 113 * Close any delays. 114 * <p> 115 * This does not call {@link #beforeRender()} on the delayed feedbacks. 116 */ 117 @Override 118 public void close() { 119 if (cycle != null) { 120 cycle.setMetaData(KEY, null); 121 cycle = null; 122 feedbacks.clear(); 123 } 124 } 125}