/** * Copyright (c) 2010, 2013 Darmstadt University of Technology. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marcel Bruch - initial API and implementation. * Olav Lenz - change to tree viewer. */ package org.eclipse.recommenders.internal.models.rcp; import static java.text.MessageFormat.format; import static org.apache.commons.io.IOCase.INSENSITIVE; import static org.apache.commons.lang3.ArrayUtils.contains; import static org.eclipse.recommenders.internal.models.rcp.Constants.*; import static org.eclipse.recommenders.internal.models.rcp.ModelsRcpModule.MODEL_CLASSIFIER; import static org.eclipse.recommenders.rcp.SharedImages.Images.*; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map.Entry; import java.util.Set; import javax.inject.Inject; import javax.inject.Named; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.UpdateValueStrategy; import org.eclipse.core.databinding.beans.PojoProperties; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionManager; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.jface.layout.TreeColumnLayout; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ColumnPixelData; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.ILazyTreeContentProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.StyledCellLabelProvider; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.TreeViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.window.Window; import org.eclipse.recommenders.coordinates.ProjectCoordinate; import org.eclipse.recommenders.internal.models.rcp.l10n.LogMessages; import org.eclipse.recommenders.internal.models.rcp.l10n.Messages; import org.eclipse.recommenders.models.IModelIndex; import org.eclipse.recommenders.models.ModelCoordinate; import org.eclipse.recommenders.models.ModelCoordinates; import org.eclipse.recommenders.models.rcp.ModelEvents.AdvisorConfigurationChangedEvent; import org.eclipse.recommenders.models.rcp.ModelEvents.ModelArchiveDownloadedEvent; import org.eclipse.recommenders.models.rcp.ModelEvents.ModelIndexOpenedEvent; import org.eclipse.recommenders.models.rcp.actions.TriggerModelDownloadForModelCoordinatesAction; import org.eclipse.recommenders.rcp.SharedImages; import org.eclipse.recommenders.rcp.SharedImages.ImageResource; import org.eclipse.recommenders.utils.Logs; import org.eclipse.recommenders.utils.Uris; import org.eclipse.recommenders.utils.rcp.Selections; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.ui.IViewSite; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.progress.UIJob; import org.osgi.service.prefs.BackingStoreException; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class ModelRepositoriesView extends ViewPart { private DataBindingContext bindingContext; private Tree tree; private Action addRemoteRepositoryAction; private final IModelIndex index; private final SharedImages images; private final EclipseModelRepository repo; private final ModelsRcpPreferences preferences; private final List<String> modelClassifiers; private final EventBus bus; private TreeViewer treeViewer; private Text txtSearch; private boolean initializeFilter = true; private ListMultimap<String, KnownCoordinate> coordinatesGroupedByRepo = LinkedListMultimap.create(); private ListMultimap<String, KnownCoordinate> filteredCoordinatesGroupedByRepo = LinkedListMultimap.create(); private final Function<ModelRepositoriesView.KnownCoordinate, String> toStringRepresentation = new Function<ModelRepositoriesView.KnownCoordinate, String>() { @Override public String apply(KnownCoordinate input) { return input.pc.toString(); } }; @Inject public ModelRepositoriesView(IModelIndex index, SharedImages images, EclipseModelRepository repo, ModelsRcpPreferences preferences, @Named(MODEL_CLASSIFIER) ImmutableSet<String> modelClassifiers, EventBus bus) { this.index = index; this.images = images; this.repo = repo; this.preferences = preferences; this.modelClassifiers = Lists.newArrayList(modelClassifiers); Collections.sort(this.modelClassifiers); this.bus = bus; } @Override public void createPartControl(Composite parent) { bus.register(this); Composite container = new Composite(parent, SWT.NONE); container.setLayout(new GridLayout()); txtSearch = new Text(container, SWT.BORDER | SWT.ICON_SEARCH | SWT.SEARCH | SWT.CANCEL); txtSearch.setMessage(Messages.SEARCH_PLACEHOLDER_FILTER_TEXT); txtSearch.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); txtSearch.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.ARROW_DOWN && tree.getItemCount() != 0) { tree.setFocus(); tree.setSelection(tree.getTopItem()); } } }); Composite composite = new Composite(container, SWT.NONE); TreeColumnLayout treeLayout = new TreeColumnLayout(); composite.setLayout(treeLayout); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); treeViewer = new TreeViewer(composite, SWT.BORDER | SWT.FULL_SELECTION | SWT.VIRTUAL); ColumnViewerToolTipSupport.enableFor(treeViewer); tree = treeViewer.getTree(); tree.setHeaderVisible(true); tree.setLinesVisible(true); TreeViewerColumn repositoryViewerColumn = new TreeViewerColumn(treeViewer, SWT.NONE); TreeColumn repositoryColumn = repositoryViewerColumn.getColumn(); treeLayout.setColumnData(repositoryColumn, new ColumnWeightData(1, ColumnWeightData.MINIMUM_WIDTH, true)); repositoryColumn.setText(Messages.COLUMN_LABEL_REPOSITORY); repositoryViewerColumn.setLabelProvider(new StyledCellLabelProvider() { @Override public void update(ViewerCell cell) { Object element = cell.getElement(); StyledString text = new StyledString(); if (element instanceof String) { String url = (String) element; text.append(Uris.toStringWithMaskedPassword(Uris.toUri(url), '*')); text.append(" "); //$NON-NLS-1$ text.append(format(Messages.TABLE_CELL_SUFFIX_KNOWN_COORDINATES, fetchNumberOfModels(url)), StyledString.COUNTER_STYLER); cell.setImage(getCellImage(url)); } if (element instanceof KnownCoordinate) { KnownCoordinate v = (KnownCoordinate) element; text.append(v.pc.toString()); } cell.setText(text.toString()); cell.setStyleRanges(text.getStyleRanges()); super.update(cell); } private Image getCellImage(String url) { URI uri = Uris.parseURI(url).orNull(); if (uri == null) { return images.getImage(OBJ_REPOSITORY); } else if (Uris.isPasswordProtected(uri)) { return images.getImage(OBJ_LOCKED_REPOSITORY); } else if (preferences.hasPassword(url)) { return images.getImage(SharedImages.Images.OBJ_LOCKED_REPOSITORY); } else { return images.getImage(SharedImages.Images.OBJ_REPOSITORY); } } }); int minWidth = calculateMinColumnWidthForClassifier(); for (String classifier : modelClassifiers) { newColumn(treeLayout, classifier, minWidth); } treeViewer.setUseHashlookup(true); treeViewer.setContentProvider(new ILazyTreeContentProvider() { @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { filteredCoordinatesGroupedByRepo = ArrayListMultimap.create(coordinatesGroupedByRepo); } @Override public void dispose() { } @Override public void updateElement(Object parent, int index) { if (parent instanceof IViewSite) { String element = preferences.remotes[index]; treeViewer.replace(parent, index, element); treeViewer.setChildCount(element, getChildren(element).length); } else if (parent instanceof String) { treeViewer.replace(parent, index, getChildren(parent)[index]); } } private Object[] getChildren(Object element) { if (element instanceof String) { return filteredCoordinatesGroupedByRepo.get((String) element).toArray(); } return new Object[0]; } @Override public void updateChildCount(Object element, int currentChildCount) { int count = 0; if (element instanceof IViewSite) { count = preferences.remotes.length; } if (contains(preferences.remotes, element)) { count = getChildren(element).length; } if (count != currentChildCount) { treeViewer.setChildCount(element, count); } } @Override public Object getParent(Object element) { if (element instanceof KnownCoordinate) { KnownCoordinate v = (KnownCoordinate) element; return v.url; } return null; } }); treeViewer.setInput(getViewSite()); refreshData(); addRemoteRepositoryAction = new Action() { @Override public void run() { addRemoteRepository(); } }; IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager(); addAction(Messages.TOOLBAR_TOOLTIP_ADD_REPOSITORY, ELCL_ADD_REPOSITORY, toolBarManager, addRemoteRepositoryAction); addAction(Messages.TOOLBAR_TOOLTIP_REFRESH, ELCL_REFRESH, toolBarManager, new Action() { @Override public void run() { refreshData(); } }); addAction(Messages.TOOLBAR_TOOLTIP_EXPAND_ALL, ELCL_EXPAND_ALL, toolBarManager, new Action() { @Override public void run() { for (int i = 0; i < treeViewer.getTree().getItemCount(); i++) { treeViewer.getTree().getItem(i).setExpanded(true); } } }); addAction(Messages.TOOLBAR_TOOLTIP_COLLAPSE_ALL, ELCL_COLLAPSE_ALL, toolBarManager, new Action() { @Override public void run() { for (int i = 0; i < treeViewer.getTree().getItemCount(); i++) { treeViewer.getTree().getItem(i).setExpanded(false); } } }); IMenuManager menuManager = getViewSite().getActionBars().getMenuManager(); addAction(Messages.MENUITEM_DELETE_MODELS, ELCL_DELETE, menuManager, new Action() { @Override public void run() { deleteCacheAndRefresh(); } }); addContextMenu(); bindingContext = initDataBindings(); } private int calculateMinColumnWidthForClassifier() { GC gc = new GC(tree); int maxLength = 0; for (String classifier : modelClassifiers) { int extent = gc.textExtent(classifier.toUpperCase()).x; if (extent > maxLength) { maxLength = extent; } } gc.dispose(); return maxLength; } private void refreshData() { new UIJob(Messages.JOB_NAME_REFRESHING_MODEL_REPOSITORIES_VIEW) { { schedule(); } @Override public IStatus runInUIThread(IProgressMonitor monitor) { Multimap<String, ModelCoordinate> fetchedCoordinates = fetchCoordinatesGroupedByRepo(); coordinatesGroupedByRepo.clear(); for (Entry<String, Collection<ModelCoordinate>> entry : fetchedCoordinates.asMap().entrySet()) { coordinatesGroupedByRepo.putAll(entry.getKey(), createCoordiantes(entry.getKey(), entry.getValue())); } treeViewer.setInput(getViewSite()); return Status.OK_STATUS; } }; } private Multimap<String, ModelCoordinate> fetchCoordinatesGroupedByRepo() { Multimap<String, ModelCoordinate> coordinatesGroupedByRepo = LinkedListMultimap.create(); for (String classifier : modelClassifiers) { addCoordinateToIndex(coordinatesGroupedByRepo, classifier); } return coordinatesGroupedByRepo; } private void addCoordinateToIndex(Multimap<String, ModelCoordinate> coordinatesGroupedByRepo, String classifier) { for (ModelCoordinate mc : index.getKnownModels(classifier)) { Optional<String> hint = mc.getHint(ModelCoordinate.HINT_REPOSITORY_URL); if (hint.isPresent()) { coordinatesGroupedByRepo.put(hint.get(), mc); } } } private List<KnownCoordinate> createCoordiantes(String url, Collection<ModelCoordinate> modelCoordinates) { Multimap<ProjectCoordinate, ModelCoordinate> coordinatesGroupedByProjectCoordinate = groupByProjectCoordinate( modelCoordinates); List<KnownCoordinate> coordinates = Lists.newArrayList(); for (ProjectCoordinate pc : coordinatesGroupedByProjectCoordinate.keySet()) { coordinates.add(new KnownCoordinate(url, pc, coordinatesGroupedByProjectCoordinate.get(pc))); } return Ordering.natural().onResultOf(toStringRepresentation).sortedCopy(coordinates); } private Multimap<ProjectCoordinate, ModelCoordinate> groupByProjectCoordinate( Collection<ModelCoordinate> modelCoordinates) { Multimap<ProjectCoordinate, ModelCoordinate> coordinatesGroupedByProjectCoordinate = LinkedListMultimap .create(); for (ModelCoordinate modelCoordinate : modelCoordinates) { coordinatesGroupedByProjectCoordinate.put(ModelCoordinates.toProjectCoordinate(modelCoordinate), modelCoordinate); } return coordinatesGroupedByProjectCoordinate; } protected int fetchNumberOfModels(String url) { return coordinatesGroupedByRepo.get(url).size(); } public class KnownCoordinate { public String url; public ProjectCoordinate pc; public Collection<ModelCoordinate> mcs; public KnownCoordinate(String url, ProjectCoordinate pc, Collection<ModelCoordinate> mcs) { this.url = url; this.pc = pc; this.mcs = mcs; } private Optional<ModelCoordinate> searchModelCoordinate(String classifier) { for (ModelCoordinate mc : mcs) { if (mc.getClassifier().equals(classifier)) { return Optional.of(mc); } } return Optional.absent(); } public boolean isDownloaded(String classifier) { Optional<ModelCoordinate> omc = searchModelCoordinate(classifier); if (omc.isPresent()) { return repo.getLocation(omc.get(), false).isPresent(); } return false; } public boolean hasModelCoordinate(String classifier) { return searchModelCoordinate(classifier).isPresent(); } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this, "mcs"); //$NON-NLS-1$ } @Override public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj, "mcs"); //$NON-NLS-1$ } } private static final class GlobMatcher implements Predicate<KnownCoordinate> { private final String glob; private GlobMatcher(String glob) { this.glob = "*" + Preconditions.checkNotNull(glob) + "*"; //$NON-NLS-1$ //$NON-NLS-2$ } @Override public boolean apply(KnownCoordinate kc) { return FilenameUtils.wildcardMatch(kc.pc.toString(), glob, INSENSITIVE); } } private void addAction(String text, ImageResource imageResource, IContributionManager contributionManager, IAction action) { action.setImageDescriptor(images.getDescriptor(imageResource)); action.setText(text); action.setToolTipText(text); contributionManager.add(action); } private void addContextMenu() { final MenuManager menuManager = new MenuManager(); Menu contextMenu = menuManager.createContextMenu(tree); menuManager.setRemoveAllWhenShown(true); tree.setMenu(contextMenu); menuManager.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(IMenuManager manager) { if (isValidType(treeViewer.getSelection(), KnownCoordinate.class)) { Set<KnownCoordinate> selectedValues = Selections.toSet(treeViewer.getSelection()); Set<ModelCoordinate> selectedModelCoordinates = Sets.newHashSet(); for (KnownCoordinate value : selectedValues) { Collection<ModelCoordinate> mcs = value.mcs; selectedModelCoordinates.addAll(mcs); } if (!selectedValues.isEmpty()) { TriggerModelDownloadForModelCoordinatesAction action = new TriggerModelDownloadForModelCoordinatesAction( Messages.MENUITEM_DOWNLOAD_MODELS, selectedModelCoordinates, repo, bus); menuManager.add(action); } } if (isValidType(treeViewer.getSelection(), String.class)) { addAction(Messages.MENUITEM_ADD_REPOSITORY, ELCL_ADD_REPOSITORY, menuManager, addRemoteRepositoryAction); final Optional<String> url = Selections.getFirstSelected(treeViewer.getSelection()); if (url.isPresent()) { addAction(Messages.MENUITEM_EDIT_REPOSITORY, ELCL_EDIT_REPOSITORY, menuManager, new Action() { @Override public void run() { editRepository(url.get()); refreshData(); } }); addAction(Messages.MENUITEM_REMOVE_REPOSITORY, ELCL_REMOVE_REPOSITORY, menuManager, new Action() { @Override public void run() { deleteRepository(url.get()); refreshData(); } }); } } } private boolean isValidType(ISelection selection, Class<?> expectedType) { return Selections.safeFirstElement(treeViewer.getSelection(), expectedType).isPresent(); } }); } private void newColumn(TreeColumnLayout treeLayout, final String classifier, int minWidth) { TreeViewerColumn classifierViewerColumn = new TreeViewerColumn(treeViewer, SWT.CENTER); TreeColumn classifierColumn = classifierViewerColumn.getColumn(); classifierColumn.setMoveable(true); classifierColumn.setResizable(false); treeLayout.setColumnData(classifierColumn, new ColumnPixelData(minWidth, false, true)); classifierColumn.setText(classifier.toUpperCase()); classifierColumn.pack(); classifierViewerColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { return null; } @Override public String getToolTipText(Object element) { if (element instanceof KnownCoordinate) { KnownCoordinate v = (KnownCoordinate) element; if (!v.hasModelCoordinate(classifier)) { return Messages.TABLE_CELL_TOOLTIP_UNAVAILABLE; } else if (v.isDownloaded(classifier)) { return Messages.TABLE_CELL_TOOLTIP_AVAILABLE_LOCALLY; } else { return Messages.TABLE_CELL_TOOLTIP_AVAILABLE_REMOTELY; } } return null; } @Override public Image getImage(Object element) { if (element instanceof KnownCoordinate) { KnownCoordinate v = (KnownCoordinate) element; if (!v.hasModelCoordinate(classifier)) { return images.getImage(OBJ_CROSS_RED); } else if (v.isDownloaded(classifier)) { return images.getImage(OBJ_CHECK_GREEN); } else { return images.getImage(OBJ_BULLET_BLUE); } } return null; } }); } @Override public void setFocus() { tree.setFocus(); } // needs to be public to work with PojoProperties public void setFilter(final String filter) { if (initializeFilter && filter.isEmpty()) { initializeFilter = false; return; } new UIJob(Messages.JOB_NAME_REFRESHING_MODEL_REPOSITORIES_VIEW) { @Override public IStatus runInUIThread(IProgressMonitor monitor) { if (!filter.isEmpty()) { filteredCoordinatesGroupedByRepo.clear(); for (String key : coordinatesGroupedByRepo.keySet()) { List<KnownCoordinate> unfiltered = coordinatesGroupedByRepo.get(key); Iterable<KnownCoordinate> filtered = Iterables.filter(unfiltered, new GlobMatcher(filter)); filteredCoordinatesGroupedByRepo.putAll(key, filtered); } } else { filteredCoordinatesGroupedByRepo = ArrayListMultimap.create(coordinatesGroupedByRepo); } refreshUI(); return Status.OK_STATUS; } }.schedule(); } private void refreshUI() { int numberOfVisibleElements = treeViewer.getTree().getSize().y / treeViewer.getTree().getItemHeight() + 1; treeViewer.refresh(); int replacedElementsCount = 0; for (int i = 0; i < preferences.remotes.length; i++) { String url = preferences.remotes[i]; List<KnownCoordinate> elements = filteredCoordinatesGroupedByRepo.get(url); treeViewer.setChildCount(url, elements.size()); for (int j = 0; j < elements.size() && replacedElementsCount < numberOfVisibleElements; j++) { treeViewer.replace(url, j, elements.get(j)); replacedElementsCount++; } treeViewer.getTree().getItem(i).setExpanded(true); } } @Override public void dispose() { super.dispose(); bindingContext.dispose(); bus.unregister(this); } @Subscribe public void onModelIndexOpened(ModelIndexOpenedEvent e) { refreshData(); } @Subscribe public void onAdvisorConfigurationChanged(AdvisorConfigurationChangedEvent e) throws IOException { refreshData(); } @Subscribe public void onModelArchiveDownloaded(final ModelArchiveDownloadedEvent e) { new UIJob(Messages.JOB_NAME_REFRESHING_MODEL_REPOSITORIES_VIEW) { @Override public IStatus runInUIThread(IProgressMonitor monitor) { KnownCoordinate key = createKey(e.model); if (key != null) { KnownCoordinate element = Iterables .tryFind(coordinatesGroupedByRepo.get(key.url), Predicates.equalTo(key)).orNull(); if (element != null) { treeViewer.update(element, null); } } return Status.OK_STATUS; } private KnownCoordinate createKey(ModelCoordinate mc) { Optional<String> remoteUrl = mc.getHint(ModelCoordinate.HINT_REPOSITORY_URL); if (remoteUrl.isPresent()) { return new KnownCoordinate(remoteUrl.get(), ModelCoordinates.toProjectCoordinate(mc), Collections.<ModelCoordinate>emptyList()); } return null; } }.schedule(); } private void addRemoteRepository() { RepositoryDetailsDialog newRepositoryDialog = new RepositoryDetailsDialog(null, null, Lists.newArrayList(preferences.remotes), preferences); if (newRepositoryDialog.open() == Window.OK) { addRepository(newRepositoryDialog.getRepositoryUrl()); } } private void deleteRepository(String remoteUrl) { ArrayList<String> newRemotes = Lists.newArrayList(preferences.remotes); newRemotes.remove(remoteUrl); storeRepositories(newRemotes); } private void addRepository(String remoteUrl) { ArrayList<String> newRemotes = Lists.newArrayList(preferences.remotes); newRemotes.add(remoteUrl); storeRepositories(newRemotes); } private void editRepository(String remoteUrl) { ArrayList<String> newRemotes = Lists.newArrayList(preferences.remotes); RepositoryDetailsDialog editRepositoryDialog = new RepositoryDetailsDialog(null, remoteUrl, newRemotes, preferences); if (editRepositoryDialog.open() == Window.OK) { String updatedRepositoryUrl = editRepositoryDialog.getRepositoryUrl(); int indexOfOriginalRepository = newRemotes.indexOf(remoteUrl); newRemotes.remove(indexOfOriginalRepository); newRemotes.add(indexOfOriginalRepository, updatedRepositoryUrl); storeRepositories(newRemotes); } } private void storeRepositories(List<String> newRemotes) { try { IEclipsePreferences s = InstanceScope.INSTANCE.getNode(BUNDLE_ID); s.put(PREF_REPOSITORY_URL_LIST, ModelsRcpPreferences.joinRemoteRepositoriesToString(newRemotes)); s.flush(); } catch (BackingStoreException e) { Logs.log(LogMessages.ERROR_FAILED_TO_STORE_REMOTE_REPOSITORY_PREFERENCES, e); } } private void deleteCacheAndRefresh() { new Job(Messages.JOB_NAME_DELETING_MODEL_CACHE) { { schedule(); } @Override protected IStatus run(IProgressMonitor monitor) { try { repo.deleteModels(); refreshData(); return Status.OK_STATUS; } catch (IOException e) { return new Status(Status.ERROR, org.eclipse.recommenders.internal.models.rcp.Constants.BUNDLE_ID, Messages.LOG_ERROR_FAILED_TO_DELETE_MODEL_CACHE); } } }; } protected DataBindingContext initDataBindings() { DataBindingContext bindingContext = new DataBindingContext(); IObservableValue search = WidgetProperties.text(SWT.Modify).observeDelayed(400, txtSearch); IObservableValue filter = PojoProperties.value("filter").observe(this); //$NON-NLS-1$ bindingContext.bindValue(filter, search, new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), null); return bindingContext; } }