Package com.google.gwt.gears.sample.gwtnote.client

Source Code of com.google.gwt.gears.sample.gwtnote.client.AppController

/*
* Copyright 2008 Google Inc.
*
* 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 com.google.gwt.gears.sample.gwtnote.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.gears.sample.gwtnote.client.local.GearsHelper;
import com.google.gwt.gears.sample.gwtnote.client.rpc.Note;
import com.google.gwt.gears.sample.gwtnote.client.rpc.NoteService;
import com.google.gwt.gears.sample.gwtnote.client.rpc.NoteServiceAsync;
import com.google.gwt.gears.sample.gwtnote.client.ui.RichTextWidget;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.Widget;

import java.util.HashMap;
import java.util.Iterator;

/**
* A controller class that manages UI and data-synchronization events. This is
* the core component of the GWT GearsNote application.
*/
public class AppController {

  protected static final String REPLACE_CONF_TEXT = "Someone has changed the note you are viewing. Keep your copy?";

  private static final int TICKS_PER_RPC = 100;

  protected boolean ready = false;

  protected boolean localDirty = false;

  private RichTextWidget rtw = null;

  private GearsHelper gears = null;

  private NoteServiceAsync rpc = null;

  private HashMap<String,Note> noteData = new HashMap<String,Note>();

  private int rpcCntdown = TICKS_PER_RPC;

  private Object lastData = "";

  /**
   * Default constructor.
   */
  public AppController() {
    // set up RPC handles
    rpc = GWT.create(NoteService.class);
    String rpcUrl = GWT.getModuleBaseURL() + "rpc";
    ((ServiceDefTarget) rpc).setServiceEntryPoint(rpcUrl);

    // create a database helper
    gears = new GearsHelper();
  }

  /**
   * Tells the controller which widget instance to use to monitor and update for
   * UI changes.
   *
   * @param mainPanel
   *          the widget to use
   */
  public void setWidget(RichTextWidget mainPanel) {
    this.rtw = mainPanel;
    rtw.addNameChangeListener(new ChangeListener() {
      public void onChange(Widget sender) {
        String newName = rtw.getName();
        if (noteData.containsKey(newName)) {
          Note n = noteData.get(newName);
          if (!rtw.getHTML().equals(n.getText())) {
            rtw.setHTML(n.getText());
          }
        }
      }
    });
  }

  /**
   * Kicks off the main processing loop. That loop is a timer that fires every
   * 10 milliseconds, forever. It calls {@link #update()} which implements the
   * sync logic, and may or may not actually do anything for a give timer tick.
   * (For instance, it may be stalled waiting on an asynchronous RPC call to
   * return.)
   */
  public void startMainLoop() {
    init();

    Timer t = new Timer() {
      @Override
      public void run() {
        if (!ready) {
          return;
        }
        update();
      }
    };
    t.scheduleRepeating(10);
  }

  /**
   * General initialization.
   */
  protected void init() {
    ready = false;
    noteData.clear(); // ditto
    syncFromGears(true);
    syncToServer(true);
   
    // init the dirty-testing code
    localDirty = false;
    Note def = noteData.get("default");
    if (def != null) {
      lastData = def.getText();
    }
  }

  /**
   * Fallback-mode initialization. This method is called when the attempt to
   * fetch data from the RPC server on startup fails; this fallback method
   * fetches similar data from the local database.
   *
   * @param isInit
   *          instructs this method that the attempt to init over RPC previously
   *          failed, which causes this method to take additional actions to
   *          prevent data loss when the server comes back online
   */
  protected void syncFromGears(boolean isInit) {
    if (!gears.gearsEnabled()) {
      return;
    }

    Note[] notes = gears.getNotes();
    Note n = null;
    if (notes != null && notes.length > 0) {
      for (int i = 0; i < notes.length; ++i) {
        n = notes[i];
        if (!noteData.containsKey(n.getName())) {
          n = notes[i];
          noteData.put(n.getName(), n);
        }
      }
    }
    if (isInit) {
      n = noteData.get("default");
      if (n != null) {
        rtw.setHTML(n.getText());
      }
    }
  }

  /**
   * Synchronizes the local application's state with the server's data. The
   * <code>gearsFallback</code> parameter should generally only be set to true
   * during first-time initialization.
   *
   * @param isInit
   *          if true, indicates that a failure to contact server should result
   *          in an attempt to sync from the Gears database
   */
  protected void syncFromServer(final boolean isInit) {
    rpc.getNotes(new AsyncCallback<Note[]>() {
      public void onFailure(Throwable caught) {
        ready = true;
      }

      public void onSuccess(Note[] notes) {
        if (notes == null) {
          ready = true;
          return;
        }

        // process the results and figure out if we need to update our state
        Note n = null;
        for (int i = 0; i < notes.length; ++i) {
          n = notes[i];

          // server sent us a totally new record -- just store it
          if (!noteData.containsKey(n.getName())) {
            noteData.put(n.getName(), n);
            gears.updateNote(n);
            continue;
          }

          // record exists -- check if server version is more recent & handle it
          Note current = noteData.get(n.getName());
          if (!current.getVersion().equals(n.getVersion())) {
            current.setVersion(n.getVersion());
            if (current.getText().equals(n.getText())) {
              // versions don't match but text is same anyway
              gears.updateNote(current); // to update version...
              localDirty = false;
              lastData = current.getText();
            } else if (current.getName().equals(rtw.getName()) && localDirty
                && Window.confirm(REPLACE_CONF_TEXT)) {
              // if versions don't match, ask user for permission to override
              gears.updateNote(current); // to update version...
              // we are proceeding w/ local data, so don't touch UI
            } else {
              // user rejected override, or else was not current note
              current.setText(n.getText());
              gears.updateNote(current);
             
              localDirty = false;
              lastData = current.getText();

              // don't forget to update UI state...
              if (rtw.getName().equals(current.getName())) {
                rtw.setHTML(current.getText());
              }
            }
            continue;
          }
        }

        // in the special case of startup, check for default value
        if (isInit) {
          Note def = noteData.get("default");
          if (def != null) {
            rtw.setHTML(def.getText());
          }
        }
        ready = true;
      }
    });
  }

  /**
   * Uploads the current set of dirty (modified) notes to the server, and upon
   * acknowledgment of that, fetches the server's set of data.
   */
  protected void syncToServer(final boolean isInit) {
    // temporarily stall the main timer loop until we're done
    ready = false;

    // convert our Map of notes into an array
    Note[] notes = new Note[noteData.size()];
    Iterator<Note> it = noteData.values().iterator();
    for (int i = 0; it.hasNext(); ++i) {
      notes[i] = it.next();
    }

    // upload our current data
    rpc.setNotes(notes, new AsyncCallback<Void>() {
      public void onFailure(Throwable caught) {
        // next call is also likely to fail, so don't bother to try
        if (isInit) {
          Note def = noteData.get("default");
          if (def != null) {
            rtw.setHeight(def.getText());
          }
        }
        ready = true;
      }

      public void onSuccess(Void result) {
        // it worked: now request server's data
        syncFromServer(isInit); // releases 'ready' later
      }
    });
  }

  /**
   * Core execution routine. Called once per timer tick.
   */
  protected void update() {
    syncFromGears(false);
    if (rpcCntdown == 0) {
      rpcCntdown = TICKS_PER_RPC;
      syncToServer(false);
    }
    rpcCntdown -= 1;
    updateUIState();
  }

  /**
   * Synchronizes the user interface state with the in-memory data model. Note
   * that this goes both ways: it updates the UI if new data is present, and
   * also updates the data if the user has entered or changed data.
   */
  protected void updateUIState() {
    // extract data from the UI
    String curName = rtw.getName();
    String curData = rtw.getHTML();
    curData = (curData == null) ? "" : curData;
    curName = (curName == null) ? "" : curName;
   
    if (!curData.equals(lastData)) {
      localDirty = true;
      lastData = curData;
    }

    if (noteData.containsKey(curName)) {
      // fetch the latest data for the note the user is trying to look at
      Note n = noteData.get(curName);
      if (!n.getText().equals(curData)) {
        // if the UI doesn't currently show latest data, update it
        n.setText(curData);
        gears.updateNote(n);
      }
    } else {
      // if the user has created a new record (unknown name) just store it
      Note n = new Note(curName, "1", curData);
      noteData.put(curName, n);
      gears.updateNote(n);
    }

    // add all the notes to the options list
    Iterator<String> it = noteData.keySet().iterator();
    String[] names = new String[noteData.size()];
    for (int i = 0; it.hasNext(); ++i) {
      names[i] = it.next();
    }
    rtw.setNameOptions(names);
  }
}
TOP

Related Classes of com.google.gwt.gears.sample.gwtnote.client.AppController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.