001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.ui;
028
029import static org.opends.messages.AdminToolMessages.*;
030
031import java.awt.Component;
032import java.awt.GridBagConstraints;
033import java.awt.GridBagLayout;
034import java.awt.Insets;
035import java.awt.event.ActionEvent;
036import java.awt.event.ActionListener;
037import java.util.ArrayList;
038import java.util.Collection;
039import java.util.HashSet;
040import java.util.List;
041import java.util.Set;
042import java.util.SortedSet;
043import java.util.TreeSet;
044
045import javax.naming.ldap.InitialLdapContext;
046import javax.swing.Box;
047import javax.swing.JCheckBox;
048import javax.swing.JComponent;
049import javax.swing.JPanel;
050import javax.swing.JScrollPane;
051import javax.swing.SwingConstants;
052import javax.swing.SwingUtilities;
053import javax.swing.border.EmptyBorder;
054import javax.swing.event.DocumentEvent;
055import javax.swing.event.DocumentListener;
056
057import org.forgerock.i18n.LocalizableMessage;
058import org.opends.guitools.controlpanel.datamodel.AbstractIndexDescriptor;
059import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
060import org.opends.guitools.controlpanel.datamodel.IndexDescriptor;
061import org.opends.guitools.controlpanel.datamodel.IndexTypeDescriptor;
062import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
063import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
064import org.opends.guitools.controlpanel.event.ScrollPaneBorderListener;
065import org.opends.guitools.controlpanel.task.DeleteIndexTask;
066import org.opends.guitools.controlpanel.task.Task;
067import org.opends.guitools.controlpanel.util.ConfigReader;
068import org.opends.guitools.controlpanel.util.Utilities;
069import org.opends.server.admin.client.ManagementContext;
070import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
071import org.opends.server.admin.client.ldap.LDAPManagementContext;
072import org.opends.server.admin.std.client.BackendCfgClient;
073import org.opends.server.admin.std.client.BackendIndexCfgClient;
074import org.opends.server.admin.std.client.PluggableBackendCfgClient;
075import org.opends.server.core.DirectoryServer;
076import org.opends.server.types.AttributeType;
077import org.opends.server.types.DN;
078import org.opends.server.types.OpenDsException;
079
080/**
081 * The panel that displays an existing index (it appears on the right of the
082 * 'Manage Indexes' dialog).
083 */
084public class IndexPanel extends AbstractIndexPanel
085{
086  private static final long serialVersionUID = 1439500626486823366L;
087
088  private IndexDescriptor index;
089  private ScrollPaneBorderListener scrollListener;
090
091  private boolean ignoreCheckSave;
092
093  private ModifyIndexTask newModifyTask;
094
095  /** Default constructor. */
096  public IndexPanel()
097  {
098    super();
099    createLayout();
100  }
101
102  /**
103   * Creates the layout of the panel (but the contents are not populated here).
104   */
105  private void createLayout()
106  {
107    GridBagConstraints gbc = new GridBagConstraints();
108    JPanel p = new JPanel(new GridBagLayout());
109    p.setOpaque(false);
110    super.createBasicLayout(p, gbc, true);
111    p.setBorder(new EmptyBorder(10, 10, 10, 10));
112    gbc = new GridBagConstraints();
113    gbc.weightx = 1.0;
114    gbc.weighty = 1.0;
115    gbc.fill = GridBagConstraints.BOTH;
116    gbc.gridx = 0;
117    gbc.gridy = 0;
118    JScrollPane scroll = Utilities.createBorderLessScrollBar(p);
119    scrollListener =
120      ScrollPaneBorderListener.createBottomBorderListener(scroll);
121    add(scroll, gbc);
122
123    gbc.gridy ++;
124    gbc.gridx = 0;
125    gbc.weightx = 1.0;
126    gbc.insets.left = 0;
127    gbc.gridwidth = 2;
128    gbc.weighty = 0.0;
129    gbc.fill = GridBagConstraints.HORIZONTAL;
130
131    gbc.insets = new Insets(10, 10, 0, 10);
132    add(warning, gbc);
133    Utilities.setWarningLabel(warning, INDEX_MODIFIED);
134
135    gbc.gridy ++;
136    JPanel buttonPanel = new JPanel(new GridBagLayout());
137    buttonPanel.setOpaque(false);
138    gbc.insets = new Insets(10, 10, 10, 10);
139    add(buttonPanel, gbc);
140
141    gbc.insets = new Insets(0, 0, 0, 0);
142    gbc.gridy = 0;
143    gbc.gridx = 0;
144    gbc.weightx = 0.0;
145    gbc.gridwidth = 1;
146    deleteIndex.setOpaque(false);
147    gbc.insets.left = 0;
148    buttonPanel.add(deleteIndex, gbc);
149    deleteIndex.addActionListener(new ActionListener()
150    {
151      @Override
152      public void actionPerformed(ActionEvent ev)
153      {
154        deleteIndex();
155      }
156    });
157    gbc.gridx = 2;
158    gbc.weightx = 1.0;
159    buttonPanel.add(Box.createHorizontalGlue(), gbc);
160    gbc.weightx = 0.0;
161    gbc.insets.left = 10;
162    saveChanges.setOpaque(false);
163    gbc.gridx = 3;
164    buttonPanel.add(saveChanges, gbc);
165    saveChanges.addActionListener(new ActionListener()
166    {
167      @Override
168      public void actionPerformed(ActionEvent ev)
169      {
170        saveIndex(false);
171      }
172    });
173
174    entryLimit.getDocument().addDocumentListener(new DocumentListener()
175    {
176      @Override
177      public void insertUpdate(DocumentEvent ev)
178      {
179        checkSaveButton();
180      }
181
182      @Override
183      public void changedUpdate(DocumentEvent ev)
184      {
185        checkSaveButton();
186      }
187
188      @Override
189      public void removeUpdate(DocumentEvent ev)
190      {
191        checkSaveButton();
192      }
193    });
194
195    ActionListener listener = new ActionListener()
196    {
197      @Override
198      public void actionPerformed(ActionEvent ev)
199      {
200        checkSaveButton();
201      }
202    };
203    for (JCheckBox cb : types)
204    {
205      cb.addActionListener(listener);
206    }
207  }
208
209  @Override
210  public LocalizableMessage getTitle()
211  {
212    return INFO_CTRL_PANEL_INDEX_PANEL_TITLE.get();
213  }
214
215  @Override
216  public Component getPreferredFocusComponent()
217  {
218    return entryLimit;
219  }
220
221  @Override
222  public void configurationChanged(ConfigurationChangeEvent ev)
223  {
224    final ServerDescriptor desc = ev.getNewDescriptor();
225    updateErrorPaneIfAuthRequired(desc,
226        isLocal() ?
227            INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_FOR_INDEX_EDITING.get() :
228      INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(desc.getHostname()));
229    SwingUtilities.invokeLater(new Runnable()
230    {
231      @Override
232      public void run()
233      {
234        checkSaveButton();
235        deleteIndex.setEnabled(!authenticationRequired(desc));
236      }
237    });
238  }
239
240  @Override
241  public void okClicked()
242  {
243  }
244
245  /**
246   * Method used to know if there are unsaved changes or not. It is used by the
247   * index selection listener when the user changes the selection.
248   *
249   * @return <CODE>true</CODE> if there are unsaved changes (and so the
250   *         selection of the index should be canceled) and <CODE>false</CODE>
251   *         otherwise.
252   */
253  public boolean mustCheckUnsavedChanges()
254  {
255    return index != null &&
256        saveChanges.isVisible() && saveChanges.isEnabled();
257  }
258
259  /**
260   * Tells whether the user chose to save the changes in the panel, to not save
261   * them or simply cancelled the selection in the tree.
262   *
263   * @return the value telling whether the user chose to save the changes in the
264   *         panel, to not save them or simply cancelled the selection change in
265   *         the tree.
266   */
267  public UnsavedChangesDialog.Result checkUnsavedChanges()
268  {
269    UnsavedChangesDialog.Result result;
270    UnsavedChangesDialog unsavedChangesDlg = new UnsavedChangesDialog(Utilities.getParentDialog(this), getInfo());
271    unsavedChangesDlg.setMessage(INFO_CTRL_PANEL_UNSAVED_CHANGES_SUMMARY.get(),
272                                 INFO_CTRL_PANEL_UNSAVED_INDEX_CHANGES_DETAILS.get(index.getName()));
273    Utilities.centerGoldenMean(unsavedChangesDlg, Utilities.getParentDialog(this));
274    unsavedChangesDlg.setVisible(true);
275    result = unsavedChangesDlg.getResult();
276    if (result == UnsavedChangesDialog.Result.SAVE)
277    {
278      saveIndex(false);
279      if (newModifyTask == null || // The user data is not valid
280          newModifyTask.getState() != Task.State.FINISHED_SUCCESSFULLY)
281      {
282        result = UnsavedChangesDialog.Result.CANCEL;
283      }
284    }
285
286    return result;
287  }
288
289  /** Checks the enabling state of the save button. */
290  private void checkSaveButton()
291  {
292    if (!ignoreCheckSave && index != null)
293    {
294      saveChanges.setEnabled(
295          !authenticationRequired(getInfo().getServerDescriptor()) &&
296          isModified());
297    }
298  }
299
300  private void deleteIndex()
301  {
302    List<LocalizableMessage> errors = new ArrayList<>();
303    ProgressDialog dlg = new ProgressDialog(
304        Utilities.createFrame(),
305        Utilities.getParentDialog(this),
306        INFO_CTRL_PANEL_DELETE_INDEX_TITLE.get(), getInfo());
307    ArrayList<AbstractIndexDescriptor> indexesToDelete = new ArrayList<>();
308    indexesToDelete.add(index);
309    DeleteIndexTask newTask = new DeleteIndexTask(getInfo(), dlg, indexesToDelete);
310    for (Task task : getInfo().getTasks())
311    {
312      task.canLaunch(newTask, errors);
313    }
314
315    if (errors.isEmpty())
316    {
317      String indexName = index.getName();
318      String backendName = index.getBackend().getBackendID();
319      if (displayConfirmationDialog(INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
320                                    INFO_CTRL_PANEL_CONFIRMATION_INDEX_DELETE_DETAILS.get(indexName, backendName)))
321      {
322        launchOperation(newTask,
323            INFO_CTRL_PANEL_DELETING_INDEX_SUMMARY.get(),
324            INFO_CTRL_PANEL_DELETING_INDEX_COMPLETE.get(),
325            INFO_CTRL_PANEL_DELETING_INDEX_SUCCESSFUL.get(indexName, backendName),
326            ERR_CTRL_PANEL_DELETING_INDEX_ERROR_SUMMARY.get(),
327            ERR_CTRL_PANEL_DELETING_INDEX_ERROR_DETAILS.get(indexName), null, dlg);
328        dlg.setVisible(true);
329      }
330    }
331    else
332    {
333      displayErrorDialog(errors);
334    }
335  }
336
337  /**
338   * Saves the index modifications.
339   *
340   * @param modal
341   *          whether the progress dialog for the task must be modal or not.
342   */
343  private void saveIndex(boolean modal)
344  {
345    newModifyTask = null;
346    if (!isModified())
347    {
348      return;
349    }
350
351    List<LocalizableMessage> errors = getErrors();
352
353    if (errors.isEmpty())
354    {
355      ProgressDialog dlg = new ProgressDialog(
356          Utilities.getFrame(this),
357          Utilities.getFrame(this),
358          INFO_CTRL_PANEL_MODIFYING_INDEX_TITLE.get(), getInfo());
359      dlg.setModal(modal);
360      newModifyTask = new ModifyIndexTask(getInfo(), dlg);
361      for (Task task : getInfo().getTasks())
362      {
363        task.canLaunch(newModifyTask, errors);
364      }
365      if (errors.isEmpty())
366      {
367        String attributeName = index.getName();
368        String backendName = index.getBackend().getBackendID();
369        launchOperation(newModifyTask,
370            INFO_CTRL_PANEL_MODIFYING_INDEX_SUMMARY.get(attributeName),
371            INFO_CTRL_PANEL_MODIFYING_INDEX_COMPLETE.get(),
372            INFO_CTRL_PANEL_MODIFYING_INDEX_SUCCESSFUL.get(attributeName, backendName),
373            ERR_CTRL_PANEL_MODIFYING_INDEX_ERROR_SUMMARY.get(),
374            ERR_CTRL_PANEL_MODIFYING_INDEX_ERROR_DETAILS.get(attributeName), null, dlg);
375        saveChanges.setEnabled(false);
376        dlg.setVisible(true);
377      }
378    }
379
380    if (!errors.isEmpty())
381    {
382      displayErrorDialog(errors);
383    }
384  }
385
386  /**
387   * Updates the contents of the panel with the provided index.
388   *
389   * @param index
390   *          the index descriptor to be used to update the panel.
391   */
392  public void update(IndexDescriptor index)
393  {
394    ignoreCheckSave = true;
395    setPrimaryValid(lEntryLimit);
396    setPrimaryValid(lType);
397    name.setText(index.getName());
398    backendName.setText(index.getBackend().getBackendID());
399    titlePanel.setDetails(LocalizableMessage.raw(index.getName()));
400    entryLimit.setText(String.valueOf(index.getEntryLimit()));
401    approximate.setSelected(false);
402    equality.setSelected(false);
403    ordering.setSelected(false);
404    substring.setSelected(false);
405    presence.setSelected(false);
406    for (IndexTypeDescriptor type : index.getTypes())
407    {
408      switch(type)
409      {
410      case APPROXIMATE:
411        approximate.setSelected(true);
412        break;
413      case PRESENCE:
414        presence.setSelected(true);
415        break;
416      case EQUALITY:
417        equality.setSelected(true);
418        break;
419      case ORDERING:
420        ordering.setSelected(true);
421        break;
422      case SUBSTRING:
423        substring.setSelected(true);
424        break;
425      }
426    }
427
428    JComponent[] comps = {entryLimit, lType, typesPanel, lEntryLimit};
429
430    for (JComponent comp : comps)
431    {
432      comp.setVisible(!index.isDatabaseIndex());
433    }
434
435    AttributeType attr = index.getAttributeType();
436    repopulateTypesPanel(attr);
437
438    if (index.isDatabaseIndex())
439    {
440      entryLimit.setText("");
441    }
442    saveChanges.setVisible(!index.isDatabaseIndex());
443    deleteIndex.setVisible(!index.isDatabaseIndex());
444    if (index.isDatabaseIndex())
445    {
446      Utilities.setWarningLabel(warning, NON_CONFIGURABLE_INDEX);
447      warning.setVisible(true);
448    }
449    else if (getInfo() != null)
450    {
451      if (getInfo().mustReindex(index))
452      {
453        Utilities.setWarningLabel(warning, INDEX_MODIFIED);
454        warning.setVisible(true);
455        warning.setVerticalTextPosition(SwingConstants.TOP);
456      }
457      else
458      {
459        warning.setVisible(false);
460      }
461    }
462    this.index = index;
463
464    ignoreCheckSave = false;
465    checkSaveButton();
466
467    scrollListener.updateBorder();
468  }
469
470  /**
471   * Returns <CODE>true</CODE> if the index has been modified and
472   * <CODE>false</CODE> otherwise.
473   *
474   * @return <CODE>true</CODE> if the index has been modified and
475   *         <CODE>false</CODE> otherwise.
476   */
477  private boolean isModified()
478  {
479    return !getTypes().equals(index.getTypes()) ||
480    !String.valueOf(index.getEntryLimit()).equals(entryLimit.getText());
481  }
482
483  /**
484   * The task in charge of modifying the index.
485   */
486  protected class ModifyIndexTask extends Task
487  {
488    private Set<String> backendSet;
489    private String attributeName;
490    private String backendName;
491    private int entryLimitValue;
492    private IndexDescriptor indexToModify;
493    private SortedSet<IndexTypeDescriptor> indexTypes = new TreeSet<>();
494    private IndexDescriptor modifiedIndex;
495
496    /**
497     * The constructor of the task.
498     *
499     * @param info
500     *          the control panel info.
501     * @param dlg
502     *          the progress dialog that shows the progress of the task.
503     */
504    public ModifyIndexTask(ControlPanelInfo info, ProgressDialog dlg)
505    {
506      super(info, dlg);
507      backendName = index.getBackend().getBackendID();
508      backendSet = new HashSet<>();
509      backendSet.add(backendName);
510      attributeName = index.getName();
511      entryLimitValue = Integer.parseInt(entryLimit.getText());
512      indexTypes = getTypes();
513
514      indexToModify = index;
515    }
516
517    @Override
518    public Type getType()
519    {
520      return Type.MODIFY_INDEX;
521    }
522
523    @Override
524    public Set<String> getBackends()
525    {
526      return backendSet;
527    }
528
529    @Override
530    public LocalizableMessage getTaskDescription()
531    {
532      return INFO_CTRL_PANEL_MODIFY_INDEX_TASK_DESCRIPTION.get(attributeName,
533          backendName);
534    }
535
536    @Override
537    public boolean canLaunch(Task taskToBeLaunched, Collection<LocalizableMessage> incompatibilityReasons)
538    {
539      boolean canLaunch = true;
540      if (state == State.RUNNING && runningOnSameServer(taskToBeLaunched))
541      {
542        // All the operations are incompatible if they apply to this
543        // backend for safety.  This is a short operation so the limitation
544        // has not a lot of impact.
545        Set<String> backends = new TreeSet<>(taskToBeLaunched.getBackends());
546        backends.retainAll(getBackends());
547        if (!backends.isEmpty())
548        {
549          incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched));
550          canLaunch = false;
551        }
552      }
553      return canLaunch;
554    }
555
556    /**
557     * Updates the configuration of the modified index.
558     *
559     * @throws OpenDsException
560     *           if there is an error updating the configuration.
561     */
562    private void updateConfiguration() throws OpenDsException
563    {
564      boolean configHandlerUpdated = false;
565      try
566      {
567        if (!isServerRunning())
568        {
569          configHandlerUpdated = true;
570          getInfo().stopPooling();
571          if (getInfo().mustDeregisterConfig())
572          {
573            DirectoryServer.deregisterBaseDN(DN.valueOf("cn=config"));
574          }
575          DirectoryServer.getInstance().initializeConfiguration(
576              org.opends.server.extensions.ConfigFileHandler.class.getName(), ConfigReader.configFile);
577          getInfo().setMustDeregisterConfig(true);
578        }
579        else
580        {
581          SwingUtilities.invokeLater(new Runnable()
582          {
583            @Override
584            public void run()
585            {
586              StringBuilder sb = new StringBuilder();
587              sb.append(getConfigCommandLineName());
588              List<String> args = getObfuscatedCommandLineArguments(getDSConfigCommandLineArguments());
589              args.removeAll(getConfigCommandLineArguments());
590
591              printEquivalentCommandLine(
592                      getConfigCommandLineName(), args, INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_MODIFY_INDEX.get());
593            }
594          });
595        }
596
597        SwingUtilities.invokeLater(new Runnable()
598        {
599          @Override
600          public void run()
601          {
602            getProgressDialog().appendProgressHtml(
603                Utilities.getProgressWithPoints(
604                    INFO_CTRL_PANEL_MODIFYING_INDEX_PROGRESS.get(attributeName),
605                    ColorAndFontConstants.progressFont));
606          }
607        });
608
609        if (isServerRunning())
610        {
611          modifyIndexOnline(getInfo().getDirContext());
612        }
613        else
614        {
615          modifyIndexOffline(backendName, attributeName, indexToModify, indexTypes, entryLimitValue);
616        }
617
618        SwingUtilities.invokeLater(new Runnable()
619        {
620          @Override
621          public void run()
622          {
623            getProgressDialog().appendProgressHtml(
624                Utilities.getProgressDone(ColorAndFontConstants.progressFont));
625          }
626        });
627      }
628      finally
629      {
630        if (configHandlerUpdated)
631        {
632          DirectoryServer.getInstance().initializeConfiguration(ConfigReader.configClassName, ConfigReader.configFile);
633          getInfo().startPooling();
634        }
635      }
636    }
637
638    /**
639     * Modifies index using the provided connection.
640     *
641     * @param ctx
642     *          the connection to be used to update the index configuration.
643     * @throws OpenDsException
644     *           if there is an error updating the server.
645     */
646    private void modifyIndexOnline(final InitialLdapContext ctx) throws OpenDsException
647    {
648      final ManagementContext mCtx = LDAPManagementContext.createFromContext(JNDIDirContextAdaptor.adapt(ctx));
649      final BackendCfgClient backend = mCtx.getRootConfiguration().getBackend(backendName);
650      modifyBackendIndexOnline((PluggableBackendCfgClient) backend);
651    }
652
653    private void modifyBackendIndexOnline(final PluggableBackendCfgClient backend) throws OpenDsException
654    {
655      final BackendIndexCfgClient index = backend.getBackendIndex(attributeName);
656      if (!indexTypes.equals(indexToModify.getTypes()))
657      {
658        index.setIndexType(IndexTypeDescriptor.toBackendIndexTypes(indexTypes));
659      }
660
661      if (entryLimitValue != index.getIndexEntryLimit())
662      {
663        index.setIndexEntryLimit(entryLimitValue);
664      }
665      index.commit();
666    }
667
668    @Override
669    protected String getCommandLinePath()
670    {
671      return null;
672    }
673
674    @Override
675    protected ArrayList<String> getCommandLineArguments()
676    {
677      return new ArrayList<>();
678    }
679
680    /**
681     * Returns the full command-line path of the dsconfig command-line if we can
682     * provide an equivalent command-line (the server is running).
683     *
684     * @return the full command-line path of the dsconfig command-line if we can
685     *         provide an equivalent command-line (the server is running).
686     */
687    private String getConfigCommandLineName()
688    {
689      if (isServerRunning() && isModified())
690      {
691        return getCommandLinePath("dsconfig");
692      }
693      else
694      {
695        return null;
696      }
697    }
698
699    @Override
700    public void runTask()
701    {
702      state = State.RUNNING;
703      lastException = null;
704
705      try
706      {
707        updateConfiguration();
708        modifiedIndex = new IndexDescriptor(attributeName,
709            indexToModify.getAttributeType(),
710            indexToModify.getBackend(),
711            indexTypes,
712            entryLimitValue);
713        getInfo().registerModifiedIndex(modifiedIndex);
714        state = State.FINISHED_SUCCESSFULLY;
715      }
716      catch (Throwable t)
717      {
718        lastException = t;
719        state = State.FINISHED_WITH_ERROR;
720      }
721    }
722
723    @Override
724    public void postOperation()
725    {
726      if (lastException == null && state == State.FINISHED_SUCCESSFULLY)
727      {
728        rebuildIndexIfNecessary(modifiedIndex, getProgressDialog());
729      }
730    }
731
732    private List<String> getDSConfigCommandLineArguments()
733    {
734      List<String> args = new ArrayList<>();
735      args.add("set-backend-index-prop");
736      args.add("--backend-name");
737      args.add(backendName);
738
739      args.add("--index-name");
740      args.add(attributeName);
741
742      if (!indexTypes.equals(indexToModify.getTypes()))
743      {
744        // To add
745        Set<IndexTypeDescriptor> toAdd = new TreeSet<>();
746        for (IndexTypeDescriptor newType : indexTypes)
747        {
748          if (!indexToModify.getTypes().contains(newType))
749          {
750            toAdd.add(newType);
751          }
752        }
753        // To delete
754        Set<IndexTypeDescriptor> toDelete = new TreeSet<>();
755        for (IndexTypeDescriptor oldType : indexToModify.getTypes())
756        {
757          if (!indexTypes.contains(oldType))
758          {
759            toDelete.add(oldType);
760          }
761        }
762        for (IndexTypeDescriptor newType : toDelete)
763        {
764          args.add("--remove");
765          args.add("index-type:" + newType);
766        }
767        for (IndexTypeDescriptor newType : toAdd)
768        {
769          args.add("--add");
770          args.add("index-type:" + newType.toBackendIndexType());
771        }
772      }
773      if (entryLimitValue != indexToModify.getEntryLimit())
774      {
775        args.add("--set");
776        args.add("index-entry-limit:"+entryLimitValue);
777      }
778      args.addAll(getConnectionCommandLineArguments());
779      args.add("--no-prompt");
780      return args;
781    }
782  }
783}