VCS: asynch load of committed changes [in "browse changes" - SVN, CVS] - works for...
authorirengrig <Irina.Chernushina@jetbrains.com>
Wed, 10 Feb 2010 08:39:18 +0000 (11:39 +0300)
committerirengrig <Irina.Chernushina@jetbrains.com>
Wed, 10 Feb 2010 08:39:18 +0000 (11:39 +0300)
19 files changed:
platform/platform-api/src/com/intellij/ui/table/TableView.java
platform/util/src/com/intellij/util/AsynchConsumer.java [new file with mode: 0644]
platform/util/src/com/intellij/util/BufferedListConsumer.java [new file with mode: 0644]
platform/util/src/com/intellij/util/ui/ListTableModel.java
platform/vcs-api/src/com/intellij/openapi/vcs/CommittedChangesProvider.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/AbstractCalledLater.java [new file with mode: 0644]
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/ChangesBrowserDialog.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesBrowser.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesTableModel.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CompositeCommittedChangesProvider.java
platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java
plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/changeBrowser/CvsChangeListsBuilder.java
plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/changeBrowser/CvsCommittedChangesProvider.java
plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/changeBrowser/LoadHistoryOperation.java
plugins/git4idea/src/git4idea/GitUtil.java
plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java
plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java
plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesTableModel.java
plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java

index 33744066c80db6b7b8deacaf573e81aa0cb2d6a9..8dc9f1d61ac90d93acdc57256ca852f63a78a1b8 100644 (file)
@@ -24,10 +24,10 @@ import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import javax.swing.event.TableModelEvent;
+import javax.swing.table.JTableHeader;
 import javax.swing.table.TableCellEditor;
 import javax.swing.table.TableCellRenderer;
 import javax.swing.table.TableColumn;
-import javax.swing.table.JTableHeader;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -149,6 +149,16 @@ public class TableView<Item> extends BaseTableView implements ItemsProvider, Sel
     return ((ListTableModel<Item>)getModel()).getItems();
   }
 
+  public void resortKeepSelection() {
+    final int column = getSelectedColumn();
+    if (column != -1) {
+      SortableColumnModel model = getListTableModel();
+      Collection selection = getSelection();
+      model.sortByColumn(column);
+      setSelection(selection);
+    }
+  }
+
   protected void onHeaderClicked(int column) {
     SortableColumnModel model = getListTableModel();
     Collection selection = getSelection();
diff --git a/platform/util/src/com/intellij/util/AsynchConsumer.java b/platform/util/src/com/intellij/util/AsynchConsumer.java
new file mode 100644 (file)
index 0000000..8792d61
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * 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.intellij.util;
+
+public interface AsynchConsumer<T> extends Consumer<T> {
+  void finished();
+}
diff --git a/platform/util/src/com/intellij/util/BufferedListConsumer.java b/platform/util/src/com/intellij/util/BufferedListConsumer.java
new file mode 100644 (file)
index 0000000..78c0589
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * 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.intellij.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class BufferedListConsumer<T> implements Consumer<List<T>> {
+  private final int mySize;
+  private final List<T> myBuffer;
+  private final Consumer<List<T>> myConsumer;
+
+  public BufferedListConsumer(int size, Consumer<List<T>> consumer) {
+    mySize = size;
+    myBuffer = new ArrayList<T>();
+    myConsumer = consumer;
+  }
+
+  public void consumeOne(final T t) {
+    consume(Collections.singletonList(t));
+  }
+
+  public void consume(List<T> list) {
+    myBuffer.addAll(list);
+    if (mySize <= myBuffer.size()) {
+      myConsumer.consume(new ArrayList<T>(myBuffer));
+      myBuffer.clear();
+    }
+  }
+
+  public void flush() {
+    if (! myBuffer.isEmpty()) {
+      myConsumer.consume(new ArrayList<T>(myBuffer));
+      myBuffer.clear();
+    }
+  }
+}
index f73bda6ce27a37895dc53ba44e31a12eee9eb126..ec8147fb49a3308bae5a5612e1b0e9b688518c30 100644 (file)
  */
 package com.intellij.util.ui;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
 
 public class ListTableModel<Item> extends TableViewModel<Item> implements ItemRemovable {
   private ColumnInfo[] myColumnInfos;
@@ -186,6 +183,12 @@ public class ListTableModel<Item> extends TableViewModel<Item> implements ItemRe
     fireTableRowsInserted(myItems.size() - 1, myItems.size() - 1);
   }
 
+  public void addRows(final Collection<Item> items) {
+    myItems.addAll(items);
+    fireTableRowsInserted(myItems.size() - items.size(), myItems.size() - 1);
+//    resort();
+  }
+
   public Object getItem(final int rowIndex) {
     return getItems().get(rowIndex);
   }
index ce08be19739a192f3fe9d09ab9eafb18fb4ac2a0..eda912583fabd3e11377f3d0838f11f89b69a820 100644 (file)
@@ -22,6 +22,7 @@ import com.intellij.openapi.vcs.changes.committed.VcsCommittedViewAuxiliary;
 import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
 import com.intellij.openapi.vcs.versionBrowser.ChangesBrowserSettingsEditor;
 import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
+import com.intellij.util.AsynchConsumer;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.List;
@@ -42,6 +43,9 @@ public interface CommittedChangesProvider<T extends CommittedChangeList, U exten
   VcsCommittedListsZipper getZipper();
 
   List<T> getCommittedChanges(U settings, RepositoryLocation location, final int maxCount) throws VcsException;
+
+  void loadCommittedChanges(U settings, RepositoryLocation location, final int maxCount, final AsynchConsumer<CommittedChangeList> consumer) throws VcsException;
+
   ChangeListColumn[] getColumns();
 
   @Nullable
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/AbstractCalledLater.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/AbstractCalledLater.java
new file mode 100644 (file)
index 0000000..76eef69
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * 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.intellij.openapi.vcs.changes.committed;
+
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.project.Project;
+
+import java.awt.*;
+
+public abstract class AbstractCalledLater implements Runnable {
+  private final Project myProject;
+  private final ModalityState myState;
+
+  protected AbstractCalledLater(final Project project, final ModalityState modalityState) {
+    myProject = project;
+    myState = modalityState;
+  }
+
+  protected AbstractCalledLater(Project project, Component component) {
+    myProject = project;
+    myState = ModalityState.stateForComponent(component);
+  }
+
+  public void callMe() {
+    final Application application = ApplicationManager.getApplication();
+    if (application.isDispatchThread()) {
+      run();
+    } else {
+      application.invokeLater(this, myState, myProject.getDisposed());
+    }
+  }
+}
index 8a87ebb65c8f0435c90414bf7eadeebde46f7154..547dc50d9049b19cb0ae4298ed74e56e8224e7bf 100644 (file)
@@ -22,9 +22,11 @@ import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.DialogWrapper;
 import com.intellij.openapi.vcs.VcsBundle;
 import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
+import com.intellij.util.AsynchConsumer;
 
 import javax.swing.*;
 import java.awt.*;
+import java.util.List;
 
 /**
  * @author max
@@ -34,6 +36,7 @@ public class ChangesBrowserDialog extends DialogWrapper {
   private CommittedChangesTableModel myChanges;
   private Mode myMode;
   private CommittedChangesBrowser myCommittedChangesBrowser;
+  private AsynchConsumer<List<CommittedChangeList>> myAppender;
 
   public enum Mode { Simple, Browse, Choose }
 
@@ -53,13 +56,46 @@ public class ChangesBrowserDialog extends DialogWrapper {
     myMode = mode;
     setTitle(VcsBundle.message("dialog.title.changes.browser"));
     setCancelButtonText(CommonBundle.getCloseButtonText());
-    if ((mode != Mode.Choose) && (ModalityState.NON_MODAL.equals(ModalityState.current()))) {
+    final ModalityState currentState = ModalityState.current();
+    if ((mode != Mode.Choose) && (ModalityState.NON_MODAL.equals(currentState))) {
       setModal(false);
     }
+    myAppender = new AsynchConsumer<List<CommittedChangeList>>() {
+
+      public void finished() {
+        new AbstractCalledLater(myProject, ModalityState.stateForComponent(myCommittedChangesBrowser)) {
+          public void run() {
+            myCommittedChangesBrowser.stopLoading();
+          }
+        }.callMe();
+      }
+
+      public void consume(final List<CommittedChangeList> committedChangeLists) {
+        new AbstractCalledLater(myProject, ModalityState.stateForComponent(myCommittedChangesBrowser)) {
+          public void run() {
+            final boolean selectFirst = (myChanges.getRowCount() == 0) && (!committedChangeLists.isEmpty());
+            myChanges.addRows(committedChangeLists);
+            if (selectFirst) {
+              myCommittedChangesBrowser.selectFirstIfAny();
+            } else {
+              myCommittedChangesBrowser.resortKeepSelection();
+            }
+          }
+        }.callMe();
+      }
+    };
 
     init();
   }
 
+  public AsynchConsumer<List<CommittedChangeList>> getAppender() {
+    return myAppender;
+  }
+
+  public void startLoading() {
+    myCommittedChangesBrowser.startLoading();
+  }
+
   protected String getDimensionServiceKey() {
     return "VCS.ChangesBrowserDialog";
   }
index 69aafde0ac38ac6ee2c7463b0fc9caa5024e0839..aed196b6a63c7d2993298ca4bc5b1d77e043ce50 100644 (file)
@@ -52,12 +52,15 @@ import java.util.List;
  */
 public class CommittedChangesBrowser extends JPanel {
   private final Project myProject;
+  // left view
   private final TableView<CommittedChangeList> myChangeListsView;
+  // right view
   private final ChangesBrowser myChangesView;
   private CommittedChangesTableModel myTableModel;
   private final JEditorPane myCommitMessageArea;
   private CommittedChangeList mySelectedChangeList;
   private final JPanel myLeftPanel;
+  private JPanel myLoadingLabelPanel;
 
   public CommittedChangesBrowser(final Project project, final CommittedChangesTableModel tableModel) {
     super(new BorderLayout());
@@ -89,7 +92,35 @@ public class CommittedChangesBrowser extends JPanel {
     commitPanel.add(separator, BorderLayout.NORTH);
 
     myLeftPanel = new JPanel(new GridBagLayout());
-    myLeftPanel.add(new JScrollPane(myChangeListsView), new GridBagConstraints(0, 0, 2, 1, 1, 1, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(2,2,2,2), 0, 0));
+    final JLabel loadingLabel = new JLabel("Loading...");
+
+    myLoadingLabelPanel = new JPanel(new BorderLayout()) {
+      @Override
+      public Dimension getPreferredSize() {
+        return new Dimension(myLoadingLabelPanel.getWidth(), loadingLabel.getHeight());
+      }
+    };
+    myLoadingLabelPanel.setBackground(UIUtil.getToolTipBackground());
+    myLoadingLabelPanel.add(loadingLabel, BorderLayout.NORTH);
+
+    final JPanel listContainer = new JPanel(new GridBagLayout());
+    final GridBagConstraints innerGb =
+      new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0);
+    ++ innerGb.gridy;
+    innerGb.weighty = 0;
+    innerGb.fill = GridBagConstraints.HORIZONTAL;
+    if (myTableModel.isAsynchLoad()) {
+      listContainer.add(myLoadingLabelPanel, innerGb);
+    }
+    ++ innerGb.gridy;
+    innerGb.weighty = 1;
+    innerGb.fill = GridBagConstraints.BOTH;
+    listContainer.add(new JScrollPane(myChangeListsView), innerGb);
+
+    final GridBagConstraints gb =
+      new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(1, 1, 1, 1), 0, 0);
+
+    myLeftPanel.add(listContainer, gb);
     if (tableModel instanceof CommittedChangesNavigation) {
       final CommittedChangesNavigation navigation = (CommittedChangesNavigation) tableModel;
 
@@ -128,12 +159,14 @@ public class CommittedChangesBrowser extends JPanel {
     JSplitPane leftSplitter = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
     leftSplitter.setTopComponent(myLeftPanel);
     leftSplitter.setBottomComponent(commitPanel);
-    leftSplitter.setDividerLocation(0.6);
-    leftSplitter.setResizeWeight(0.5);
+    leftSplitter.setDividerLocation(0.8);
+    leftSplitter.setResizeWeight(0.8);
 
     JSplitPane splitter = new JSplitPane();
     splitter.setLeftComponent(leftSplitter);
     splitter.setRightComponent(myChangesView);
+    splitter.setDividerLocation(0.5);
+    splitter.setResizeWeight(0.5);
 
     add(splitter, BorderLayout.CENTER);
 
@@ -142,12 +175,18 @@ public class CommittedChangesBrowser extends JPanel {
     myChangesView.getDiffAction().registerCustomShortcutSet(CommonShortcuts.getDiff(), myChangeListsView);
   }
 
-  private void selectFirstIfAny() {
+  public void selectFirstIfAny() {
     if (myTableModel.getRowCount() > 0) {
       TableUtil.selectRows(myChangeListsView, new int[]{0});
     }
   }
 
+  public void resortKeepSelection() {
+    if (myTableModel.getRowCount() > 0) {
+      myChangeListsView.resortKeepSelection();
+    }
+  }
+
   public void addToolBar(JComponent toolBar) {
     myLeftPanel.add(toolBar, BorderLayout.NORTH);
   }
@@ -192,4 +231,12 @@ public class CommittedChangesBrowser extends JPanel {
   public void setTableContextMenu(final ActionGroup group) {
     PopupHandler.installPopupHandler(myChangeListsView, group, ActionPlaces.UNKNOWN, ActionManager.getInstance());
   }
+
+  public void startLoading() {
+  }
+
+  public void stopLoading() {
+    myLoadingLabelPanel.setVisible(false);
+    myLoadingLabelPanel.repaint();
+  }
 }
index f5510fc9d096877c96e8d86c2454638914a91463..d9647fcb9b917085e41d85328c1d41ca83e280c5 100644 (file)
@@ -33,14 +33,17 @@ import java.util.Comparator;
 import java.util.List;
 
 public class CommittedChangesTableModel extends ListTableModel<CommittedChangeList> {
+  private final boolean myAsynchLoad;
   private static final ChangeListColumn[] ourDefaultColumns = new ChangeListColumn[] { ChangeListColumn.DATE, ChangeListColumn.NAME };
 
-  public CommittedChangesTableModel(final List<CommittedChangeList> changeLists) {
+  public CommittedChangesTableModel(final List<CommittedChangeList> changeLists, boolean asynchLoad) {
     super(buildColumnInfos(ourDefaultColumns), changeLists, 0);
+    myAsynchLoad = asynchLoad;
   }
 
-  public CommittedChangesTableModel(final List<CommittedChangeList> changeLists, final ChangeListColumn[] columns) {
+  public CommittedChangesTableModel(final List<CommittedChangeList> changeLists, final ChangeListColumn[] columns, boolean asynchLoad) {
     super(buildColumnInfos(columns), changeLists, 0);
+    myAsynchLoad = asynchLoad;
   }
 
   public void sortByChangesColumn(final ChangeListColumn column, int sortingType) {
@@ -94,4 +97,8 @@ public class CommittedChangesTableModel extends ListTableModel<CommittedChangeLi
       return myColumn;
     }
   }
+
+  public boolean isAsynchLoad() {
+    return myAsynchLoad;
+  }
 }
index b94a5de869daad9ecc20bd73b9994611e88ae6ad..f7467391a721010f66fc3e11d6e7a839b708bb0c 100644 (file)
@@ -23,6 +23,7 @@ import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
 import com.intellij.openapi.vcs.versionBrowser.ChangesBrowserSettingsEditor;
 import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
 import com.intellij.openapi.vcs.versionBrowser.DateFilterComponent;
+import com.intellij.util.AsynchConsumer;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.Nullable;
@@ -86,6 +87,13 @@ public class CompositeCommittedChangesProvider implements CommittedChangesProvid
     throw new UnsupportedOperationException();
   }
 
+  public void loadCommittedChanges(CompositeChangeBrowserSettings settings,
+                                   RepositoryLocation location,
+                                   int maxCount,
+                                   AsynchConsumer<CommittedChangeList> consumer) throws VcsException {
+    throw new UnsupportedOperationException();
+  }
+
   public ChangeListColumn[] getColumns() {
     Set<ChangeListColumn> columns = new LinkedHashSet<ChangeListColumn>();
     for(AbstractVcs vcs: myBaseVcss) {
index 66b4a45b0962ea024520fa261824db06954f6b40..979c75e0a61abc6d466b1d1791d2c52ad6797e60 100644 (file)
 package com.intellij.openapi.vcs.impl;
 
 import com.intellij.ide.actions.CloseTabToolbarAction;
-import com.intellij.ide.errorTreeView.HotfixData;
 import com.intellij.ide.errorTreeView.ErrorTreeElementKind;
+import com.intellij.ide.errorTreeView.HotfixData;
 import com.intellij.ide.errorTreeView.SimpleErrorData;
 import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.application.Application;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.command.CommandProcessor;
@@ -29,18 +30,20 @@ import com.intellij.openapi.diff.*;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.fileTypes.FileType;
 import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.progress.ProgressIndicator;
 import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.DialogWrapper;
 import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.util.Comparing;
 import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.util.Getter;
-import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.vcs.*;
 import com.intellij.openapi.vcs.annotate.Annotater;
 import com.intellij.openapi.vcs.annotate.AnnotationProvider;
 import com.intellij.openapi.vcs.annotate.FileAnnotation;
+import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
 import com.intellij.openapi.vcs.changes.Change;
 import com.intellij.openapi.vcs.changes.committed.*;
 import com.intellij.openapi.vcs.changes.ui.*;
@@ -61,6 +64,8 @@ import com.intellij.ui.content.Content;
 import com.intellij.ui.content.ContentFactory;
 import com.intellij.ui.content.ContentManager;
 import com.intellij.ui.content.MessageView;
+import com.intellij.util.AsynchConsumer;
+import com.intellij.util.BufferedListConsumer;
 import com.intellij.util.Consumer;
 import com.intellij.util.ContentsUtil;
 import com.intellij.util.ui.ConfirmationDialog;
@@ -394,7 +399,21 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper {
   }
 
   public void showChangesBrowser(List<CommittedChangeList> changelists, @Nls String title) {
-    showChangesBrowser(new CommittedChangesTableModel(changelists), title, false, null);
+    showChangesBrowser(new CommittedChangesTableModel(changelists, false), title, false, null);
+  }
+
+  private ChangesBrowserDialog createChangesBrowserDialog(CommittedChangesTableModel changelists,
+                                  String title,
+                                  boolean showSearchAgain,
+                                  @Nullable final Component parent) {
+    final ChangesBrowserDialog.Mode mode = showSearchAgain ? ChangesBrowserDialog.Mode.Browse : ChangesBrowserDialog.Mode.Simple;
+    final ChangesBrowserDialog dlg = parent != null
+                                     ? new ChangesBrowserDialog(myProject, parent, changelists, mode)
+                                     : new ChangesBrowserDialog(myProject, changelists, mode);
+    if (title != null) {
+      dlg.setTitle(title);
+    }
+    return dlg;
   }
 
   private void showChangesBrowser(CommittedChangesTableModel changelists,
@@ -454,42 +473,33 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper {
       if (myProject.isDefault() || (ProjectLevelVcsManager.getInstance(myProject).getAllActiveVcss().length == 0) ||
           (! ModalityState.NON_MODAL.equals(ModalityState.current()))) {
         final List<CommittedChangeList> versions = new ArrayList<CommittedChangeList>();
-        final List<VcsException> exceptions = new ArrayList<VcsException>();
-        final Ref<CommittedChangesTableModel> tableModelRef = new Ref<CommittedChangesTableModel>();
 
-        final ChangeBrowserSettings settings1 = settings;
-        final boolean done = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
-          public void run() {
-            try {
-              versions.addAll(provider.getCommittedChanges(settings1, location, 0));
-            }
-            catch (VcsException e) {
-              exceptions.add(e);
-            }
-            tableModelRef.set(new CommittedChangesTableModel(versions, provider.getColumns()));
-          }
-        }, VcsBundle.message("browse.changes.progress.title"), true, myProject);
+        if (parent == null || !parent.isValid()) {
+          parent = WindowManager.getInstance().suggestParentWindow(myProject);
+        }
+        final CommittedChangesTableModel model = new CommittedChangesTableModel(versions, true);
+        final ChangesBrowserDialog dlg = createChangesBrowserDialog(model, title, filterUI != null, parent);
 
-        if (!done) return;
+        final AsynchronousListsLoader task = new AsynchronousListsLoader(myProject, provider, location, settings, dlg);
+        ProgressManager.getInstance().run(task);
+        dlg.show();
+        dlg.startLoading();
+        task.cancel();
 
-        if (!exceptions.isEmpty()) {
+        final List<VcsException> exceptions = task.getExceptions();
+        if (! exceptions.isEmpty()) {
           Messages.showErrorDialog(myProject, VcsBundle.message("browse.changes.error.message", exceptions.get(0).getMessage()),
                                    VcsBundle.message("browse.changes.error.title"));
           return;
         }
 
-        if (versions.isEmpty()) {
+        if (! task.isRevisionsReturned()) {
           Messages.showInfoMessage(myProject, VcsBundle.message("browse.changes.nothing.found"),
                                    VcsBundle.message("browse.changes.nothing.found.title"));
-          return;
-        }
-
-        if (parent == null || !parent.isValid()) {
-          parent = WindowManager.getInstance().suggestParentWindow(myProject);
         }
-        showChangesBrowser(tableModelRef.get(), title, filterUI != null, parent);
       }
       else {
+        // todo
         openCommittedChangesTab(provider, location, settings, 0, title);
       }
     }
@@ -506,7 +516,7 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper {
       return null;
     }
     final ChangesBrowserDialog dlg = new ChangesBrowserDialog(myProject, new CommittedChangesTableModel((List<CommittedChangeList>)changes,
-                                                                                                        provider.getColumns()),
+                                                                                                        provider.getColumns(), false),
                                                                          ChangesBrowserDialog.Mode.Choose);
     dlg.show();
     if (dlg.isOK()) {
@@ -590,4 +600,75 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper {
     }
   }
 
+  private static class AsynchronousListsLoader extends Task.Backgroundable {
+    private final CommittedChangesProvider myProvider;
+    private final RepositoryLocation myLocation;
+    private final ChangeBrowserSettings mySettings;
+    private final ChangesBrowserDialog myDlg;
+    private final List<VcsException> myExceptions;
+    private volatile boolean myCanceled;
+    private boolean myRevisionsReturned;
+
+    private AsynchronousListsLoader(@Nullable Project project, final CommittedChangesProvider provider,
+                                    final RepositoryLocation location, final ChangeBrowserSettings settings, final ChangesBrowserDialog dlg) {
+      super(project, VcsBundle.message("browse.changes.progress.title"), true, BackgroundFromStartOption.getInstance());
+      myProvider = provider;
+      myLocation = location;
+      mySettings = settings;
+      myDlg = dlg;
+      myExceptions = new LinkedList<VcsException>();
+    }
+
+    public void cancel() {
+      myCanceled = true;
+    }
+
+    @Override
+    public void run(@NotNull final ProgressIndicator indicator) {
+      final AsynchConsumer<List<CommittedChangeList>> appender = myDlg.getAppender();
+      final BufferedListConsumer<CommittedChangeList> bufferedListConsumer = new BufferedListConsumer<CommittedChangeList>(10, appender);
+
+      final Application application = ApplicationManager.getApplication();
+      try {
+        myProvider.loadCommittedChanges(mySettings, myLocation, 0, new AsynchConsumer<CommittedChangeList>() {
+          public void consume(CommittedChangeList committedChangeList) {
+            myRevisionsReturned = true;
+            bufferedListConsumer.consumeOne(committedChangeList);
+            if (myCanceled) {
+              indicator.cancel();
+            }
+          }
+
+          public void finished() {
+            bufferedListConsumer.flush();
+            appender.finished();
+
+            if (! myRevisionsReturned) {
+              application.invokeLater(new Runnable() {
+                public void run() {
+                  myDlg.close(-1);
+                }
+              }, ModalityState.stateForComponent(myDlg.getWindow()));
+            }
+          }
+        });
+      }
+      catch (VcsException e) {
+        myExceptions.add(e);
+        application.invokeLater(new Runnable() {
+          public void run() {
+            myDlg.close(-1);
+          }
+        }, ModalityState.stateForComponent(myDlg.getWindow()));
+      }
+    }
+
+    public List<VcsException> getExceptions() {
+      return myExceptions;
+    }
+
+    public boolean isRevisionsReturned() {
+      return myRevisionsReturned;
+    }
+  }
 }
index 9e4a0b5dfc164d523cda5ed92dcc883d52e4f7aa..11771b5c511cbe15ec0bedc1b72b4737663ced8c 100644 (file)
@@ -19,10 +19,10 @@ import com.intellij.cvsSupport2.connections.CvsEnvironment;
 import com.intellij.cvsSupport2.history.CvsRevisionNumber;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
 import org.netbeans.lib.cvsclient.command.log.Revision;
 import org.netbeans.lib.cvsclient.command.log.SymbolicName;
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.annotations.NonNls;
 
 import java.util.*;
 
@@ -87,7 +87,7 @@ public class CvsChangeListsBuilder {
     return result;
   }
 
-  private void addRevision(RevisionWrapper revision) {
+  public CvsChangeList addRevision(RevisionWrapper revision) {
     final Revision cvsRevision = revision.getRevision();
     CvsChangeList version = findOrCreateVersionFor(cvsRevision.getMessage(),
                                                    revision.getTime(),
@@ -96,6 +96,7 @@ public class CvsChangeListsBuilder {
                                                    revision.getFile());
 
     version.addFileRevision(revision);
+    return version;
   }
 
   private CvsChangeList findOrCreateVersionFor(final String message, final long date, final String author,
@@ -121,24 +122,33 @@ public class CvsChangeListsBuilder {
     return result;
   }
 
+  @Nullable
+  public RevisionWrapper revisionWrapperFromLog(final LogInformationWrapper log) {
+    final String file = log.getFile();
+    if (CvsChangeList.isAncestor(myRootPath, file)) {
+      for (Revision revision : log.getRevisions()) {
+        if (revision != null) {
+          if (revision.getState().equals(CvsChangeList.DEAD_STATE) &&
+              revision.getMessage().indexOf(INITIALLY_ADDED_ON_BRANCH) >= 0) {
+            // ignore dead revision (otherwise it'll get stuck in incoming changes forever - it's considered a deletion and
+            // the file is never actually deleted)
+            continue;
+          }
+          String branchName = getBranchName(revision, log.getSymbolicNames());
+          return new RevisionWrapper(stripAttic(file), revision, branchName);
+        }
+      }
+    }
+    return null;
+  }
+
   public void addLogs(final List<LogInformationWrapper> logs) {
     List<RevisionWrapper> revisionWrappers = new ArrayList<RevisionWrapper>();
 
     for (LogInformationWrapper log : logs) {
-      final String file = log.getFile();
-      if (CvsChangeList.isAncestor(myRootPath, file)) {
-        for (Revision revision : log.getRevisions()) {
-          if (revision != null) {
-            if (revision.getState().equals(CvsChangeList.DEAD_STATE) &&
-                revision.getMessage().indexOf(INITIALLY_ADDED_ON_BRANCH) >= 0) {
-              // ignore dead revision (otherwise it'll get stuck in incoming changes forever - it's considered a deletion and
-              // the file is never actually deleted)              
-              continue;
-            }
-            String branchName = getBranchName(revision, log.getSymbolicNames());
-            revisionWrappers.add(new RevisionWrapper(stripAttic(file), revision, branchName));
-          }
-        }
+      final RevisionWrapper revisionWrapper = revisionWrapperFromLog(log);
+      if (revisionWrapper != null) {
+        revisionWrappers.add(revisionWrapper);
       }
     }
 
index 866a6a2ea86c82ce9330ba1ba6c865ef99a85408..3fe68b23dc23e7a52b68fffcd4dfd164447c38d3 100644 (file)
@@ -38,6 +38,8 @@ import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
 import com.intellij.openapi.vcs.versionBrowser.ChangesBrowserSettingsEditor;
 import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.AsynchConsumer;
+import com.intellij.util.Consumer;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -133,6 +135,49 @@ public class CvsCommittedChangesProvider implements CachingCommittedChangesProvi
     return loadCommittedChanges(settings, cvsLocation.getModuleName(), cvsLocation.getEnvironment(), cvsLocation.getRootFile());
   }
 
+  public void loadCommittedChanges(ChangeBrowserSettings settings,
+                                   RepositoryLocation location,
+                                   int maxCount,
+                                   final AsynchConsumer<CommittedChangeList> consumer)
+    throws VcsException {
+    try {
+      CvsRepositoryLocation cvsLocation = (CvsRepositoryLocation) location;
+      final String module = cvsLocation.getModuleName();
+      final CvsEnvironment connectionSettings = cvsLocation.getEnvironment();
+      if (connectionSettings.isOffline()) {
+        return;
+      }
+      final CvsChangeListsBuilder builder = new CvsChangeListsBuilder(module, connectionSettings, myProject, cvsLocation.getRootFile());
+      Date dateTo = settings.getDateBeforeFilter();
+      Date dateFrom = settings.getDateAfterFilter();
+      if (dateFrom == null) {
+        final Calendar calendar = Calendar.getInstance();
+        calendar.set(1970, 2, 2);
+        dateFrom = calendar.getTime();
+      }
+      final ChangeBrowserSettings.Filter filter = settings.createFilter();
+      final CvsResult executionResult = runRLogOperation(connectionSettings, module, dateFrom, dateTo, new Consumer<LogInformationWrapper>() {
+        public void consume(LogInformationWrapper wrapper) {
+          final RevisionWrapper revisionWrapper = builder.revisionWrapperFromLog(wrapper);
+          final CvsChangeList changeList = builder.addRevision(revisionWrapper);
+          if (filter.accepts(changeList)) {
+            consumer.consume(changeList);
+          }
+        }
+      });
+
+      if (executionResult.isCanceled()) {
+        throw new ProcessCanceledException();
+      }
+      else if (!executionResult.hasNoErrors()) {
+        throw executionResult.composeError();
+      }
+    }
+    finally {
+      consumer.finished();
+    }
+  }
+
   private List<CvsChangeList> loadCommittedChanges(final ChangeBrowserSettings settings,
                                                    final String module,
                                                    final CvsEnvironment connectionSettings,
@@ -165,6 +210,44 @@ public class CvsCommittedChangesProvider implements CachingCommittedChangesProvi
     }
   }
 
+  private CvsResult runRLogOperation(final CvsEnvironment settings,
+                                     final String module,
+                                     final Date dateFrom,
+                                     final Date dateTo,
+                                     final Consumer<LogInformationWrapper> consumer) {
+    final CvsResult executionResult = runRLogOperationImpl(settings, module, dateFrom, dateTo, consumer);
+
+    for (VcsException error : executionResult.getErrors()) {
+      for (String message : error.getMessages()) {
+        if (message.indexOf(INVALID_OPTION_S) >= 0) {
+          LoadHistoryOperation.doesNotSuppressEmptyHeaders(settings);
+          // try only once
+          return runRLogOperationImpl(settings, module, dateFrom, dateTo, consumer);
+        }
+      }
+    }
+    return executionResult;
+  }
+
+  private CvsResult runRLogOperationImpl(final CvsEnvironment settings,
+                                     final String module,
+                                     final Date dateFrom,
+                                     final Date dateTo,
+                                     final Consumer<LogInformationWrapper> consumer) {
+    LoadHistoryOperation operation = new LoadHistoryOperation(settings, module, dateFrom, dateTo, null) {
+      @Override
+      protected void wrapperAdded(final LogInformationWrapper wrapper) {
+        consumer.consume(wrapper);
+      }
+    };
+
+    CvsOperationExecutor executor = new CvsOperationExecutor(myProject);
+    executor.performActionSync(new CommandCvsHandler(CvsBundle.message("browse.changes.load.history.progress.title"), operation),
+                               CvsOperationExecutorCallback.EMPTY);
+
+    return executor.getResult();
+  }
+
   private CvsResult runRLogOperation(final CvsEnvironment settings,
                                      final String module,
                                      final Date dateFrom,
index ee53ab837b202100368511d2b5ce852fb69ef862..bd00bc8460eb003f132dd30e5d32a32dc9758289 100644 (file)
@@ -45,9 +45,9 @@ public class LoadHistoryOperation extends LocalPathIndifferentOperation {
   public LoadHistoryOperation(CvsEnvironment environment, String module,
                               @NotNull Date dateFrom,
                               @Nullable Date dateTo,
-                              final List<LogInformationWrapper> log) {
+                              @Nullable final List<LogInformationWrapper> log) {
     super(environment);
-    myLog = log;
+    myLog = log == null ? new ArrayList<LogInformationWrapper>() : log;
     myModule = module;
     myDateFrom = dateFrom;
     myDateTo = dateTo;
@@ -86,10 +86,14 @@ public class LoadHistoryOperation extends LocalPathIndifferentOperation {
       LogInformationWrapper wrapper = LogInformationWrapper.wrap(myEnvironment.getRepository(), logInfo);
       if (wrapper != null) {
         myLog.add(wrapper);
+        wrapperAdded(wrapper);
       }
     }
   }
 
+  protected void wrapperAdded(final LogInformationWrapper wrapper) {
+  }
+
   public boolean runInReadThread() {
     return false;
   }
index 3e2261334d0ecbff7048e950c84ebebb0bd108a9..55ec70779737415df17ac69bac976c187350eb8b 100644 (file)
@@ -654,9 +654,10 @@ public class GitUtil {
     return getPossibleBase(file, n - 1, path);
   }
 
-  public static List<CommittedChangeList> getLocalCommittedChanges(final Project project,
+  public static void getLocalCommittedChanges(final Project project,
                                                                    final VirtualFile root,
-                                                                   final Consumer<GitSimpleHandler> parametersSpecifier)
+                                                                   final Consumer<GitSimpleHandler> parametersSpecifier,
+                                                                   final Consumer<CommittedChangeList> consumer)
     throws VcsException {
     final List<CommittedChangeList> rc = new ArrayList<CommittedChangeList>();
 
@@ -670,11 +671,25 @@ public class GitUtil {
     StringScanner s = new StringScanner(output);
     while (s.hasMoreData() && s.startsWith('\u000C')) {
       s.nextLine();
-      rc.add(GitChangeUtils.parseChangeList(project, root, s));
+      consumer.consume(GitChangeUtils.parseChangeList(project, root, s));
     }
     if (s.hasMoreData()) {
       throw new IllegalStateException("More input is avaialble: " + s.line());
     }
+  }
+
+  public static List<CommittedChangeList> getLocalCommittedChanges(final Project project,
+                                                                   final VirtualFile root,
+                                                                   final Consumer<GitSimpleHandler> parametersSpecifier)
+    throws VcsException {
+    final List<CommittedChangeList> rc = new ArrayList<CommittedChangeList>();
+
+    getLocalCommittedChanges(project, root, parametersSpecifier, new Consumer<CommittedChangeList>() {
+      public void consume(CommittedChangeList committedChangeList) {
+        rc.add(committedChangeList);
+      }
+    });
+
     return rc;
   }
 
index 7089ef1f0a98acc800161e49633f6da20f451e7c..ac9c428f7eff703d4f0983504c5ef279d6808a74 100644 (file)
@@ -28,6 +28,7 @@ import com.intellij.openapi.vcs.versionBrowser.ChangesBrowserSettingsEditor;
 import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.AsynchConsumer;
 import com.intellij.util.Consumer;
 import git4idea.GitBranch;
 import git4idea.GitRemote;
@@ -40,6 +41,7 @@ import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
@@ -130,11 +132,39 @@ public class GitCommittedChangeListProvider implements CachingCommittedChangesPr
     return null;
   }
 
+  public void loadCommittedChanges(ChangeBrowserSettings settings,
+                                   RepositoryLocation location,
+                                   int maxCount,
+                                   AsynchConsumer<CommittedChangeList> consumer)
+    throws VcsException {
+    try {
+      getCommittedChangesImpl(settings, location, maxCount, consumer);
+    }
+    finally {
+      consumer.finished();
+    }
+  }
+
   /**
    * {@inheritDoc}
    */
   public List<CommittedChangeList> getCommittedChanges(ChangeBrowserSettings settings, RepositoryLocation location, final int maxCount)
     throws VcsException {
+
+    final List<CommittedChangeList> result = new ArrayList<CommittedChangeList>();
+
+    getCommittedChangesImpl(settings, location, maxCount, new Consumer<CommittedChangeList>() {
+      public void consume(CommittedChangeList committedChangeList) {
+        result.add(committedChangeList);
+      }
+    });
+
+    return result;
+  }
+
+  private void getCommittedChangesImpl(ChangeBrowserSettings settings, RepositoryLocation location, final int maxCount,
+                                                            final Consumer<CommittedChangeList> consumer)
+    throws VcsException {
     GitRepositoryLocation l = (GitRepositoryLocation)location;
     final Long beforeRev = settings.getChangeBeforeFilter();
     final Long afterRev = settings.getChangeBeforeFilter();
@@ -146,7 +176,7 @@ public class GitCommittedChangeListProvider implements CachingCommittedChangesPr
       throw new VcsException("The repository does not exists anymore: " + l.getRoot());
     }
 
-    return GitUtil.getLocalCommittedChanges(myProject, root, new Consumer<GitSimpleHandler>() {
+    GitUtil.getLocalCommittedChanges(myProject, root, new Consumer<GitSimpleHandler>() {
       public void consume(GitSimpleHandler h) {
         if (!StringUtil.isEmpty(author)) {
           h.addParameters("--author=" + author);
@@ -170,10 +200,9 @@ public class GitCommittedChangeListProvider implements CachingCommittedChangesPr
           h.addParameters(GitUtil.formatLongRev(afterRev) + "..");
         }
       }
-    });
+    }, consumer);
   }
 
-
   /**
    * {@inheritDoc}
    */
index 3069bc5859b69746a1921686e3daaed99f85ea50..bace8c3ad22a0c50ccc7e1548270aac879fbe5e0 100644 (file)
@@ -32,6 +32,7 @@ import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
 import com.intellij.openapi.vcs.versionBrowser.ChangesBrowserSettingsEditor;
 import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.AsynchConsumer;
 import com.intellij.util.Consumer;
 import com.intellij.util.containers.MultiMap;
 import com.intellij.util.messages.MessageBusConnection;
@@ -167,6 +168,45 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi
     }
   }
 
+  public void loadCommittedChanges(ChangeBrowserSettings settings,
+                                   RepositoryLocation location,
+                                   int maxCount,
+                                   final AsynchConsumer<CommittedChangeList> consumer)
+    throws VcsException {
+    try {
+      final SvnRepositoryLocation svnLocation = (SvnRepositoryLocation) location;
+      final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
+      if (progress != null) {
+        progress.setText(SvnBundle.message("progress.text.changes.collecting.changes"));
+        progress.setText2(SvnBundle.message("progress.text2.changes.establishing.connection", location));
+      }
+
+      final String repositoryRoot;
+      try {
+        final SVNRepository repository = myVcs.createRepository(svnLocation.getURL());
+        repositoryRoot = repository.getRepositoryRoot(true).toString();
+        repository.closeSession();
+      }
+      catch (SVNException e) {
+        throw new VcsException(e);
+      }
+
+      final ChangeBrowserSettings.Filter filter = settings.createFilter();
+
+      getCommittedChangesImpl(settings, svnLocation.getURL(), new String[]{""}, maxCount, new Consumer<SVNLogEntry>() {
+        public void consume(final SVNLogEntry svnLogEntry) {
+          final SvnChangeList cl = new SvnChangeList(myVcs, svnLocation, svnLogEntry, repositoryRoot);
+          if (filter.accepts(cl)) {
+            consumer.consume(cl);
+          }
+        }
+      });
+    }
+    finally {
+      consumer.finished();
+    }
+  }
+
   public List<SvnChangeList> getCommittedChanges(ChangeBrowserSettings settings, final RepositoryLocation location, final int maxCount) throws VcsException {
     final SvnRepositoryLocation svnLocation = (SvnRepositoryLocation) location;
     final ArrayList<SvnChangeList> result = new ArrayList<SvnChangeList>();
@@ -232,6 +272,7 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi
         revisionAfter = SVNRevision.create(1);
       }
 
+      // todo log in committed provider
       logger.doLog(SVNURL.parseURIEncoded(url), filterUrls, revisionBefore, revisionBefore, revisionAfter,
                    settings.STOP_ON_COPY, true, maxCount,
                    new ISVNLogEntryHandler() {
index 6755e3cf372cedb365144b7d4d4ac108727afdae..776a27fe32d2730832aa068f03d74239c994c99e 100644 (file)
@@ -30,7 +30,7 @@ public class SvnCommittedChangesTableModel extends CommittedChangesTableModel im
 
   public SvnCommittedChangesTableModel(final SvnRepositoryLocation location, final Project project, final VirtualFile vcsRoot,
                                        final ChangeListColumn[] columns) throws VcsException {
-    super(new ArrayList<CommittedChangeList>(), columns);
+    super(new ArrayList<CommittedChangeList>(), columns, false);
     myMediator = new SvnRevisionsNavigationMediator(location, project, vcsRoot);
     setItems(myMediator.getCurrent());
   }
index 503be9ae84e8819cbdd47867aba99e2568eca80d..73430fe5e331bd5502dba0c9127cc4bdb551f222 100644 (file)
@@ -271,6 +271,7 @@ public class SvnHistoryProvider implements VcsHistoryProvider {
     SVNLogClient client = myVcs.createLogClient();
     final boolean supports15 = SvnUtil.checkRepositoryVersion15(myVcs, root);
     supports15Ref.set(supports15);
+    // todo log in history provider
     client.doLog(myURL, new String[] {}, SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNRevision.create(1), false, true, supports15, 0, null,
                  new RepositoryLogEntryHandler(url, SVNRevision.UNDEFINED, relativeUrl, result));
   }