Previous Page
Next Page

7.5. Saving View State

Up to this point, the Favorites view contains only the current list of projects when the Eclipse session starts up. Items can be added to the Favorites view during the course of the session, but as soon as Eclipse is shut down, the changes are lost. In addition, the view's sort and filter information should be saved so that the view will be returned to the same state when the session is restarted. To accomplish all this, two different mechanisms are used.

7.5.1. Saving local view information

Eclipse provides a memento-based mechanism for saving view and editor state information. In this case, this mechanism is good for saving the sorting and filter state of a view because that information is specific to each individual view. It is not good for saving global information shared by multiple views, so that is tackled in the next section.

To save the sorting state, two methods should be added to the FavoritesViewSorter. The first method saves the current sort state as an instance of IMemento by converting the sort order and ascending/descending state into an XML-like structure. The second method takes a very guarded approach to reading and resetting the sort order and ascending/descending state from IMemento so that the sort state will be valid even if IMemento is not what was expected.

private static final String TAG_DESCENDING = "descending";
private static final String TAG_COLUMN_INDEX = "columnIndex";
private static final String TAG_TYPE = "SortInfo";
private static final String TAG_TRUE = "true";

public void saveState(IMemento memento) {
   for (int i = 0; i < infos.length; i++) {
      SortInfo info = infos[i];
      IMemento mem = memento.createChild(TAG_TYPE);
      mem.putInteger(TAG_COLUMN_INDEX, info.columnIndex);
      if (info.descending)
         mem.putString(TAG_DESCENDING, TAG_TRUE);
   }
}

public void init(IMemento memento) {
   List newInfos = new ArrayList(infos.length);
   IMemento[] mems = memento.getChildren(TAG_TYPE);
   for (int i = 0; i < mems.length; i++) {
      IMemento mem = mems[i];
      Integer value = mem.getInteger(TAG_COLUMN_INDEX);
      if (value == null)
         continue;
      int index = value.intValue();
      if (index < 0 || index >= infos.length)
         continue;
      SortInfo info = infos[index];
      if (newInfos.contains(info))
         continue;
      info.descending =
         TAG_TRUE.equals(mem.getString(TAG_DESCENDING));
      newInfos.add(info);
    }
    for (int i = 0; i < infos.length; i++)
       if (!newInfos.contains(infos[i]))
          newInfos.add(infos[i]);
    infos = (SortInfo[]) newInfos.toArray(
       new SortInfo[newInfos.size()]);
}

In addition to saving the sort state, the filter state needs to be saved. This is accomplished by adding the following two methods to the FavoritesViewFilterAction type.

public void saveState(IMemento memento) {
   nameFilter.saveState(memento);
}

public void init(IMemento memento) {
   nameFilter.init(memento);
}

Then add two new methods to FavoritesViewNameFilter:

private static final String TAG_PATTERN = "pattern";
private static final String TAG_TYPE = "NameFilterInfo";

public void saveState(IMemento memento) {
   if (pattern.length() == 0)
      return;
   IMemento mem = memento.createChild(TAG_TYPE);
   mem.putString(TAG_PATTERN, pattern);
}

public void init(IMemento memento) {
   IMemento mem = memento.getChild(TAG_TYPE);
   if (mem == null)
      return;
   setPattern(mem.getString(TAG_PATTERN));
}

These new methods are hooked to the view by adding the following field and methods to the FavoritesView.

private IMemento memento;

public void saveState(IMemento memento) {
   super.saveState(memento);
   sorter.saveState(memento);
   filterAction.saveState(memento);
}

public void init(IViewSite site, IMemento memento)
   throws PartInitException
{
   super.init(site, memento);
   this.memento = memento;
}

The sorting and filter state cannot be restored immediately in the init() method shown above because the part control has not been created. Instead, the method caches IMemento for use later during the initialization process. You must then modify both the createTableSorter() method and the createViewPulldownMenu() method as shown next to restore the sorting and filter state before associating the sorter with the viewer and the filter action with the menu, respectively.

private void createTableSorter() {

   ... same code as in Section 7.2.6 on page 278 ...

   if (memento != null)
      sorter.init(memento);
   viewer.setSorter(sorter);
}
private void createViewPulldownMenu() {

   ... same code as in a Section 7.3.4 on page 287 ...

   if (memento != null)
      filterAction.init(memento);
   menu.add(filterAction);
}

Eclipse stores all memento-based view and editor state information in a single file:

<workspace>\.metadata\.plugins\org.eclipse.ui.workbench\workbench.xml

For example (reformatted so that it's easier to read):

<views>
  <view
    id="com.qualityeclipse.favorites.views.FavoritesView"
    partName="Favorites">
    <viewState>
      <SortInfo columnIndex="0" descending="true"/>
       <SortInfo columnIndex="1"/>
       <SortInfo columnIndex="2"/>
     </viewState>
  </view>
  <view id="org.eclipse.ui.views.TaskList" partName="Tasks">
    <viewState
      columnWidth0="19" columnWidth1="19" columnWidth2="288"
      columnWidth3="108" columnWidth4="216" columnWidth5="86"
      horizontalPosition="0" verticalPosition="0">
      <selection/>
     </viewState>
  </view>
  ...
</views>

7.5.2. Saving global view information

Now you need to save the state of the FavoritesManager, which is shared by all Favorites views. For this to occur, augment the FavoritesPlugin, the FavoritesManager, and each Favorites item with the ability to save their information so that they can be recreated later. In the FavoritesPlugin, augment the stop() method to call a new saveFavorites() method in the FavoritesManager.

FavoritesManager.getManager().saveFavorites();

The existing loadFavorites() method in the FavoritesManager must be revised as follows and new methods added so that the Favorites items will be lazily loaded when needed. Lazy initialization is the Eclipse theme, so the list will not be built until it is needed. In addition, a new saveFavorites() method must be added to store the Favorites items so that they can be restored when Eclipse is restarted.

private static final String TAG_FAVORITES = "Favorites";
private static final String TAG_FAVORITE = "Favorite";
private static final String TAG_TYPEID = "TypeId";
private static final String TAG_INFO = "Info";

private void loadFavorites() {
   favorites = new HashSet(20);
   FileReader reader = null;
   try {
      reader = new FileReader (getFavoritesFile());
      loadFavorites(XMLMemento.createReadRoot(reader));
   }
   catch (FileNotFoundException e) {
      // Ignored... no Favorites items exist yet.
   }
   catch (Exception e) {
      // Log the exception and move on.
      FavoritesLog.logError(e);
   }
   finally {
      try {
         if (reader != null) reader.close();
      } catch (IOException e) {
         FavoritesLog.logError(e);
      }
   }
}

private void loadFavorites(XMLMemento memento) {
   IMemento [] children = memento.getChildren(TAG_FAVORITE);
   for (int i = 0; i < children.length; i++) {
      IFavoriteItem item =
         newFavoriteFor(
            children[i].getString(TAG_TYPEID),
            children[i].getString(TAG_INFO));
      if (item != null)
         favorites.add(item);
   }
}

public IFavoriteItem newFavoriteFor(String typeId, String info) {
   FavoriteItemType[] types = FavoriteItemType.getTypes();
   for (int i = 0; i < types.length; i++)
      if (types[i].getId().equals(typeId))
         return types[i].loadFavorite(info);
   return null;
}
public void saveFavorites() {
   if (favorites == null)
      return;
   XMLMemento memento = XMLMemento.createWriteRoot(TAG_FAVORITES);
   saveFavorites(memento);
   FileWriter writer = null;
   try {
      writer = new FileWriter(getFavoritesFile());
      memento.save(writer);
   }
   catch (IOException e) {
      FavoritesLog.logError(e);
   }
   finally {
      try {
         if (writer != null)
            writer.close();
      }
      catch (IOException e) {
         FavoritesLog.logError(e);
      }
   }
}

private void saveFavorites(XMLMemento memento) {
   Iterator iter = favorites.iterator();
   while (iter.hasNext()) {
      IFavoriteItem item = (IFavoriteItem) iter.next();
      IMemento child = memento.createChild(TAG_FAVORITE);
      child.putString(TAG_TYPEID, item.getType().getId());
      child.putString(TAG_INFO, item.getInfo());
   }
}

private File getFavoritesFile() {
   return FavoritesPlugin
      .getDefault()
      .getStateLocation()
      .append("favorites.xml")
      .toFile();
}

The load and save methods interact with a file named favorites.xml, which is located in the following workspace metadata subdirectory: <workspace>\.metadata\.plugins\com.qualityeclipse.favorites. The file content is in XML format and might look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<Favorites>
   <Favorite
      Info="/First Project/com/qualityeclipse/sample
         /HelloWorld.java"
      TypeId="WBFile"/>
   <Favorite
      Info="/com.qualityeclipse.favorites/src"
      TypeId="WBFolder"/>
   ...
</Favorites>

Tip

Eclipse can crash or lock up...not often, if ever, but it can. If it does, then the normal shutdown sequence is preempted and your plug-in will not get a chance to save its model state. To protect your data, you can register a save participant (ISaveParticipant) and store critical model states ("snapshots") at various times during the Eclipse session. The mechanism is the same as that used to receive resource change events when your plug-in is inactive (see Section 9.5, Delayed Changed Events, on page 387).



Previous Page
Next Page