001    package org.hackystat.projectbrowser.page.loadingprocesspanel;
002    
003    import org.apache.wicket.ResourceReference;
004    import org.apache.wicket.ajax.AjaxRequestTarget;
005    import org.apache.wicket.ajax.AjaxSelfUpdatingTimerBehavior;
006    import org.apache.wicket.markup.html.basic.MultiLineLabel;
007    import org.apache.wicket.markup.html.image.Image;
008    import org.apache.wicket.markup.html.panel.Panel;
009    import org.apache.wicket.model.PropertyModel;
010    import org.apache.wicket.util.time.Duration;
011    
012    /**
013     * Panel to show progress of a lengthy operation.
014     * This panel will show when process is in progress or process end not successfully.
015     * This panel will display the processingMessage.
016     * This panel will update every second via Ajax when process is in progress.
017     * For more detail, please see Processable interface.
018     * 
019     * @author Shaoxuan Zhang
020     */
021    public class LoadingProcessPanel extends Panel {
022      /** Support serialization. */
023      private static final long serialVersionUID = 1L;
024      /** the data model. */
025      private Processable dataModel;
026      
027      /**
028       * @param id the wicket component id.
029       * @param model the data model associating with this panel.
030       */
031      // Has to suppress this warning because this constructor calls start() which calls onFinish which
032      // is an overridable method. Calling start() is necessary to start the Ajax auto update function
033      // if process is undergoing when every time this panel is created. The onFinish() method is not
034      // actually called when constructing. It is called within the timers interface method and will be
035      // called later.
036      @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
037      public LoadingProcessPanel(String id, Processable model) {
038        super(id);
039        this.dataModel = model;
040        MultiLineLabel label = 
041          new MultiLineLabel("processingMessage", new PropertyModel(dataModel, "processingMessage"));
042        label.setOutputMarkupId(true);
043        add(label);
044        
045        //add loading image
046        Image loadingImage = new Image("loadingImage", 
047            new ResourceReference(LoadingProcessPanel.class, "loading21-1.gif")) {
048          /** Support serialization. */
049          private static final long serialVersionUID = 1L;
050          @Override
051          public boolean isVisible() {
052            return dataModel.isInProcess();
053          }
054        };
055        add(loadingImage);
056    
057        if (dataModel.isInProcess()) {
058          start();
059        }
060      }
061    
062      /**
063       * start panel self update.
064       */
065      public final void start() {
066        final AjaxSelfUpdatingTimerBehavior selfUpdate = 
067          new AjaxSelfUpdatingTimerBehavior(Duration.milliseconds(500)) {
068          /** Support serialization. */
069          private static final long serialVersionUID = 1L;
070          @Override
071          protected void onPostProcessTarget(AjaxRequestTarget target) {
072            if (!dataModel.isInProcess()) {
073              // do custom action
074              onFinished(target);
075              // stop the self update
076              stop();
077            }
078          }
079        };
080        add(selfUpdate);
081      }
082      /**
083       * visible when data loading is in process or data loading end with error.
084       * @return true if this panel is visible.
085       */
086      @Override
087      public boolean isVisible() {
088        return dataModel.isInProcess() || !dataModel.isComplete();
089      }
090      
091      /**
092       * The method called when loading process is completed.
093       * @param target an AjaxRequestTarget.
094       */
095      protected void onFinished(AjaxRequestTarget target) {
096        //do nothing here. add implementation in subclasses.
097      }
098    }