Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.tips.core/src/org/eclipse/tips/core/TipProvider.java')
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/TipProvider.java332
1 files changed, 332 insertions, 0 deletions
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProvider.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProvider.java
new file mode 100644
index 000000000..fde1da7b3
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProvider.java
@@ -0,0 +1,332 @@
+/****************************************************************************
+ * Copyright (c) 2017, 2018 Remain Software
+ * 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:
+ * Wim Jongman <wim.jongman@remainsoftware.com> - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.tips.core;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.tips.core.internal.FinalTip;
+
+/**
+ * Class to provide tips to the tip framework. It is the job of this provider to
+ * manage its tips. Examples of managing tips are:
+ *
+ * <ul>
+ * <li>Loading tips from the internet</li>
+ * <li>Serve next, previous and current tip on request</li>
+ * </ul>
+ *
+ * After the TipProvider is instantiated by the {@link ITipManager}, the
+ * TipManager will insert itself by calling {@link #setManager(ITipManager)}.
+ * Then the TipManager will asynchronous call this providers'
+ * {@link #loadNewTips(IProgressMonitor)} method. The job of the load() method
+ * is to do long work like fetching new tips from the internet and storing them
+ * locally. There is no defined method on how tips should be stored locally,
+ * implementers are free to do what is needed.
+ *
+ * The constructor must return fast, meaning that tips may not be fetched from
+ * the Internet in the constructor. This should be done in the
+ * {@link #loadNewTips(IProgressMonitor)} method.
+ *
+ * To indicate that this provider is ready to serve tips, it should call the
+ * {@link #setTips(List)} method which then sets its <code>ready</code> flag.
+ *
+ */
+public abstract class TipProvider {
+
+ private ITipManager fTipManager;
+ private int fTipIndex;
+ protected List<Tip> fTips = new ArrayList<>();
+ private Tip fCurrentTip;
+ private boolean fReady;
+ private TipProviderListenerManager fListenerManager = new TipProviderListenerManager();
+ private Tip fFinalTip = new FinalTip(getID());
+ private String fExpression;
+
+ /**
+ * The zero argument constructor must be able to instantiate the TipProvider.
+ * This method may also be used to quickly set the available tips by calling the
+ * {@link #setTips(List)} method. The constructor may not be used to load tips
+ * from the internet. Use the {@link #loadNewTips(IProgressMonitor)} method for
+ * this purpose.
+ *
+ * @see #loadNewTips(IProgressMonitor)
+ * @see #setTips(List)
+ */
+ public TipProvider() {
+ }
+
+ /**
+ * Provides the opportunity to release all held resources.
+ */
+ public abstract void dispose();
+
+ /**
+ * @return the short description of this provider.
+ */
+ public abstract String getDescription();
+
+ /**
+ * @return the ID of this provider
+ */
+ public abstract String getID();
+
+ /**
+ * The image used by the UI for low resolution
+ *
+ * @return a 48x48 {@link TipImage}
+ */
+ public abstract TipImage getImage();
+
+ /**
+ * Get a list of tips. The default implementation returns tips based on the
+ * following conditions: <br>
+ * <dl>
+ * <dt><code>pFilter</code> is false</dt>
+ * <dd>Return all read and unread tips.</dd>
+ * <dt><code>pFilter</code> is true</dt>
+ * <dd>Return read and unread tips if the tipManager may serve unread tips,
+ * otherwise return only unread tips.</dd>
+ * </dl>
+ * <p>
+ * Subclasses may override (calling super(false) to fetch the list) if they want
+ * to serve or sort the list of tips in a different way.
+ *
+ * @param filter
+ * false or true, see description above.
+ * @return an unmodifiable list of tips.
+ */
+ public synchronized List<Tip> getTips(boolean filter) {
+ if (filter) {
+ return Collections.unmodifiableList(fTips //
+ .stream() //
+ .filter(tip -> getManager().mustServeReadTips() || !getManager().isRead(tip)) //
+ .sorted(Comparator.comparing(Tip::getCreationDate).reversed()) //
+ .collect(Collectors.toList()));
+ }
+ return Collections.unmodifiableList(fTips);
+ }
+
+ /**
+ * @return the {@link Tip} that was last returned by {@link #getNextTip()} or
+ * {@link #getPreviousTip()}
+ */
+ public synchronized Tip getCurrentTip() {
+ if (fCurrentTip == null) {
+ return getNextTip();
+ }
+ return fCurrentTip;
+ }
+
+ /**
+ * The next {@link Tip} is returned based on the read status of the Tip and the
+ * fact if already read tips must be served or not which is known by the
+ * {@link ITipManager}: ({@link ITipManager#mustServeReadTips()}).
+ *
+ * @return the next {@link Tip}
+ * @see #getPreviousTip()
+ * @see #getCurrentTip()
+ */
+ public synchronized Tip getNextTip() {
+ boolean unreadOnly = !getManager().mustServeReadTips();
+ List<Tip> list = getTips(unreadOnly);
+ if (list.isEmpty()) {
+ return setCurrentTip(fFinalTip);
+ }
+ if (!unreadOnly && fCurrentTip != null) {
+ fTipIndex++;
+ } else if (fCurrentTip != null && getManager().isRead(fCurrentTip)) {
+ fTipIndex++;
+ }
+ if (fTipIndex >= list.size()) {
+ fTipIndex = 0;
+ }
+ return setCurrentTip(list.get(fTipIndex));
+ }
+
+ /**
+ * @return the previous {@link Tip}
+ * @see #getNextTip()
+ * @see #getCurrentTip()
+ */
+ public Tip getPreviousTip() {
+ List<Tip> list = getTips(!getManager().mustServeReadTips());
+ if (list.isEmpty()) {
+ return setCurrentTip(fFinalTip);
+ }
+ fTipIndex--;
+ if (fTipIndex < 0) {
+ fTipIndex = list.size() - 1;
+ }
+ return setCurrentTip(list.get(fTipIndex));
+ }
+
+ /**
+ * @return the {@link ITipManager} of this provider, never null.
+ */
+ public synchronized ITipManager getManager() {
+ return fTipManager;
+ }
+
+ /**
+ * @return true if the provider is ready to deliver tips
+ */
+ public final boolean isReady() {
+ return fReady;
+ }
+
+ /**
+ * Is called asynchronously during startup of the TipManager to gather new tips.
+ *
+ * The provider is not available to the UI unless it has called it's
+ * {@link #setTips(List)} method. It is therefore possible that the provider is
+ * not immediately visible in the tip UI but will be added later.
+ * <p>
+ * If you run out of tips and you feel that you should load more tips on your
+ * own then you can also asynchronously call this method. A good place would be
+ * to override {@link #getTips(boolean)}, check if the supply of tips is
+ * sufficient and then call this method asynchronously.
+ * <p>
+ * One strategy is to do a long running fetch in this method and then store the
+ * tips locally. On the next run of the TipManager, the fetched tips can be
+ * served from the constructor (i.e. by calling {@link #setTips(List)}), making
+ * them available immediately
+ *
+ * @param monitor
+ * The monitor to report back progress.
+ * @return the status in case you want to report problems.
+ * @see TipProvider#setTips(List)
+ * @see TipProvider#isReady()
+ */
+ public abstract IStatus loadNewTips(IProgressMonitor monitor);
+
+ private synchronized Tip setCurrentTip(Tip pTip) {
+ fCurrentTip = pTip;
+ return fCurrentTip;
+ }
+
+ /**
+ * Sets the TipManager. You should probably not call this method directly. This
+ * method is normally called after the provider is instantiated by the
+ * {@link ITipManager}. If you create the provider yourself you should register
+ * the provider with {@link ITipManager#register(TipProvider)} which in turn
+ * will call this method. Subclasses may override but must not forget to call
+ * super in order to save the {@link ITipManager}.
+ *
+ * @param tipManager
+ * the {@link ITipManager}
+ * @return this
+ */
+ public synchronized TipProvider setManager(ITipManager tipManager) {
+ fTipManager = tipManager;
+ return this;
+ }
+
+ /**
+ * Sets the tips for this provider, replacing the current set of tips, and sets
+ * the <code>ready</code> flag to true. This method is typically called from the
+ * constructor of the {@link TipProvider} but may also be called from the
+ * asynchronous {@link #loadNewTips(IProgressMonitor)} method.
+ *
+ * @param tips
+ * a list of {@link Tip} objects
+ * @return this
+ * @see #addTips(List)
+ * @see #isReady()
+ * @see #loadNewTips(IProgressMonitor)
+ */
+ public TipProvider setTips(List<Tip> tips) {
+ if(getManager().isDisposed()) {
+ return this;
+ }
+ doSetTips(tips, true);
+ fReady = true;
+ fListenerManager.notifyListeners(TipProviderListener.EVENT_READY, this);
+ return this;
+ }
+
+ /**
+ * Adds the passed tips to the set of tips this provider already has sets the
+ * <code>ready</code> flag to true. This method is typically called from the
+ * constructor of the {@link TipProvider} but may also be called from the
+ * asynchronous {@link #loadNewTips(IProgressMonitor)} method.
+ *
+ * @param tips
+ * a list of {@link Tip} objects
+ * @return this
+ * @see #setTips(List)
+ * @see #isReady()
+ * @see #loadNewTips(IProgressMonitor)
+ */
+ public TipProvider addTips(List<Tip> tips) {
+ doSetTips(tips, false);
+ fReady = true;
+ fListenerManager.notifyListeners(TipProviderListener.EVENT_READY, this);
+ return this;
+ }
+
+ private synchronized void doSetTips(List<Tip> tips, boolean replace) {
+ if (replace) {
+ fTips.clear();
+ }
+ fTips.addAll(tips);
+ }
+
+ /**
+ * Gets the listener manager so that interested parties can subscribe to the
+ * events of this provider.
+ *
+ * @return the {@link TipProviderListenerManager}
+ */
+ public TipProviderListenerManager getListenerManager() {
+ return fListenerManager;
+ }
+
+ /**
+ * Returns an expression that is used by the {@link ITipManager} to determine
+ * the priority of this provider. The expression can be used to advice the
+ * TipManager when the tips of this provider deserve priority. The Eclipse IDE
+ * TipManager uses the core expression from the o.e.core.runtime bundle.
+ * Example: The expression
+ *
+ * <pre>
+ * &lt;with
+ * variable="activeWorkbenchWindow.activePerspective"&gt;
+ * &lt;equals value="org.eclipse.jdt.ui.JavaPerspective"&gt;&lt;/equals&gt;
+ * &lt;/with&gt;
+ * </pre>
+ *
+ * will give the provider priority when the java perspective is active in the
+ * IDE
+ *
+ * @return the expression which can be empty or null.
+ */
+ public String getExpression() {
+ return fExpression;
+ }
+
+ /**
+ * Sets the expression to determine the priority of the provider.
+ *
+ * @param expression
+ * the expression, may be null.
+ *
+ * @see #getExpression()
+ */
+ public void setExpression(String expression) {
+ fExpression = expression;
+ }
+} \ No newline at end of file

Back to the top