1 /*******************************************************************************
2  * Copyright (c) 2003, 2017 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Trevor S. Kaufman <endante@gmail.com> - bug 156152
14  *******************************************************************************/
15 package org.eclipse.team.internal.ui.synchronize;
16 
17 import java.text.DateFormat;
18 import java.util.Calendar;
19 import java.util.Date;
20 
21 import org.eclipse.core.runtime.Adapters;
22 import org.eclipse.core.runtime.jobs.Job;
23 import org.eclipse.osgi.util.NLS;
24 import org.eclipse.team.internal.ui.TeamUIMessages;
25 import org.eclipse.team.internal.ui.TeamUIPlugin;
26 import org.eclipse.team.ui.synchronize.ISynchronizeParticipant;
27 import org.eclipse.ui.IMemento;
28 import org.eclipse.ui.actions.ActionFactory;
29 
30 /**
31  * Schedule to refresh a subscriber at a specified interval. The schedule can be disabled or enabled
32  * and will create the refresh job.
33  *
34  * @since 3.0
35  */
36 public class SubscriberRefreshSchedule {
37 	private long refreshInterval = 3600; // 1 hour default
38 	private Date refreshStart;
39 	private boolean runOnce = false;
40 
41 	private boolean enabled = false;
42 
43 	private RefreshParticipantJob job;
44 
45 	private IRefreshable refreshable;
46 
47 	private IRefreshEvent lastRefreshEvent;
48 
49 	/**
50 	 * Key for settings in memento
51 	 */
52 	private static final String CTX_REFRESHSCHEDULE_INTERVAL = TeamUIPlugin.ID + ".CTX_REFRESHSCHEDULE_INTERVAL"; //$NON-NLS-1$
53 
54 	/**
55 	 * Key for schedule in memento
56 	 */
57 	private static final String CTX_REFRESHSCHEDULE_ENABLED = TeamUIPlugin.ID + ".CTX_REFRESHSCHEDULE_ENABLED"; //$NON-NLS-1$
58 
59 	/**
60 	 * Key for start date in memento
61 	 */
62 	private static final String CTX_REFRESHSCHEDULE_START = TeamUIPlugin.ID + ".CTX_REFRESHSCHEDULE_START"; //$NON-NLS-1$
63 
64 	/**
65 	 * Key for run once in memento
66 	 */
67 	private static final String CTX_REFRESHSCHEDULE_RUNONCE = TeamUIPlugin.ID + ".CTX_REFRESHSCHEDULE_RUNONCE"; //$NON-NLS-1$
68 
69 	private IRefreshSubscriberListener refreshSubscriberListener = new IRefreshSubscriberListener() {
70 		@Override
71 		public void refreshStarted(IRefreshEvent event) {
72 		}
73 		@Override
74 		public ActionFactory.IWorkbenchAction refreshDone(final IRefreshEvent event) {
75 			if (getRefreshable(event.getParticipant()) == refreshable) {
76 				lastRefreshEvent = event;
77 				if(enabled && event.getRefreshType() == IRefreshEvent.SCHEDULED_REFRESH) {
78 					RefreshUserNotificationPolicy policy = new RefreshUserNotificationPolicy(refreshable.getParticipant());
79 					policy.refreshDone(event);
80 				}
81 			}
82 			return null;
83 		}
84 		private IRefreshable getRefreshable(ISynchronizeParticipant participant) {
85 			return Adapters.adapt(participant, IRefreshable.class);
86 		}
87 	};
88 
89 
SubscriberRefreshSchedule(IRefreshable refreshable)90 	public SubscriberRefreshSchedule(IRefreshable refreshable) {
91 		this.refreshable = refreshable;
92 		RefreshParticipantJob.addRefreshListener(refreshSubscriberListener);
93 
94 //		Calendar cal = Calendar.getInstance();
95 //		cal.clear();
96 //		refreshStart = cal.getTime();
97 	}
98 
99 	/**
100 	 * @return Returns the enabled.
101 	 */
isEnabled()102 	public boolean isEnabled() {
103 		return enabled;
104 	}
105 
106 	/**
107 	 * @param enabled The enabled to set.
108 	 * @param allowedToStart Is the job allowed to start.
109 	 */
setEnabled(boolean enabled, boolean allowedToStart)110 	public void setEnabled(boolean enabled, boolean allowedToStart) {
111 		boolean wasEnabled = isEnabled();
112 		this.enabled = enabled;
113 		if(enabled && ! wasEnabled) {
114 			if(allowedToStart) {
115 				startJob();
116 			}
117 		} else {
118 			stopJob();
119 		}
120 	}
121 
122 	/**
123 	 * @return Returns the refreshInterval in seconds.
124 	 */
getRefreshInterval()125 	public long getRefreshInterval() {
126 		return refreshInterval;
127 	}
128 
getParticipant()129 	public ISynchronizeParticipant getParticipant() {
130 		return refreshable.getParticipant();
131 	}
132 
133 	/**
134 	 * @param refreshInterval The refreshInterval to set.
135 	 */
setRefreshInterval(long refreshInterval)136 	public void setRefreshInterval(long refreshInterval) {
137 		if(refreshInterval != getRefreshInterval()) {
138 			stopJob();
139 			this.refreshInterval = refreshInterval;
140 			runOnce = false;
141 			if(isEnabled()) {
142 				startJob();
143 			}
144 		}
145 	}
146 
startJob()147 	public void startJob() {
148 		if(job == null) {
149 			job = refreshable.createJob(getRefreshIntervalAsString());
150 			job.setUser(false);
151 		} else if(job.getState() != Job.NONE){
152 			stopJob();
153 		}
154 		job.setRefreshInterval(getRefreshInterval());
155 		job.setRestartOnCancel(true);
156 		job.setReschedule(!runOnce);
157 		if (refreshStart != null) {
158 			job.schedule(getJobDelay());
159 		} else {
160 			job.schedule();
161 		}
162 	}
163 
164 	/**
165 	 * @return schedule delay in milliseconds
166 	 */
getJobDelay()167 	private long getJobDelay() {
168 		Calendar now = Calendar.getInstance();
169 		Calendar start = Calendar.getInstance();
170 		start.setTime(refreshStart);
171 		while (now.after(start)) {
172 			start.add(Calendar.DATE, 1); // tomorrow
173 		}
174 		return start.getTimeInMillis() - now.getTimeInMillis();
175 	}
176 
stopJob()177 	protected void stopJob() {
178 		if(job != null) {
179 			job.setRestartOnCancel(false /* don't restart the job */);
180 			job.setReschedule(false);
181 			job.cancel();
182 			job = null;
183 		}
184 	}
185 
dispose()186 	public void dispose() {
187 		stopJob();
188 		RefreshParticipantJob.removeRefreshListener(refreshSubscriberListener);
189 	}
190 
saveState(IMemento memento)191 	public void saveState(IMemento memento) {
192 		memento.putString(CTX_REFRESHSCHEDULE_ENABLED, Boolean.toString(enabled));
193 		memento.putInteger(CTX_REFRESHSCHEDULE_INTERVAL, (int)refreshInterval);
194 		if (refreshStart != null)
195 			memento.putString(CTX_REFRESHSCHEDULE_START, Long.toString(refreshStart.getTime()));
196 		memento.putString(CTX_REFRESHSCHEDULE_RUNONCE, Boolean.toString(runOnce));
197 	}
198 
init(IMemento memento, IRefreshable refreshable)199 	public static SubscriberRefreshSchedule init(IMemento memento, IRefreshable refreshable) {
200 		SubscriberRefreshSchedule schedule = new SubscriberRefreshSchedule(refreshable);
201 		if(memento != null) {
202 			String enabled = memento.getString(CTX_REFRESHSCHEDULE_ENABLED);
203 			int interval = memento.getInteger(CTX_REFRESHSCHEDULE_INTERVAL).intValue();
204 			String startString = memento.getString(CTX_REFRESHSCHEDULE_START);
205 			String runOnce = memento.getString(CTX_REFRESHSCHEDULE_RUNONCE);
206 			if (startString != null) {
207 				long start = Long.parseLong(startString);
208 				schedule.setRefreshStartTime(new Date(start));
209 			}
210 			schedule.setRefreshInterval(interval);
211 			schedule.setRunOnce("true".equals(runOnce) ? true : false); //$NON-NLS-1$
212 			schedule.setEnabled("true".equals(enabled) ? true : false, false /* don't start job */); //$NON-NLS-1$
213 		}
214 		// Use the defaults if a schedule hasn't been saved or can't be found.
215 		return schedule;
216 	}
217 
refreshEventAsString(IRefreshEvent event)218 	public static String refreshEventAsString(IRefreshEvent event) {
219 		if(event == null) {
220 			return TeamUIMessages.SyncViewPreferencePage_lastRefreshRunNever;
221 		}
222 		long stopMills = event.getStopTime();
223 		StringBuilder text = new StringBuilder();
224 		if(stopMills <= 0) {
225 			text.append(TeamUIMessages.SyncViewPreferencePage_lastRefreshRunNever);
226 		} else {
227 			Date lastTimeRun = new Date(stopMills);
228 			text.append(DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(lastTimeRun));
229 		}
230 		int changeCount = event.getChangeDescription().getChangeCount();
231 		if (changeCount == 0) {
232 			text.append(TeamUIMessages.RefreshSchedule_7);
233 		} else if (changeCount == 1) {
234 			text.append(NLS.bind(TeamUIMessages.RefreshSchedule_changesSingular, new String[] { Integer.toString(changeCount) }));
235 		} else {
236 			text.append(NLS.bind(TeamUIMessages.RefreshSchedule_changesPlural, new String[] { Integer.toString(changeCount) }));
237 		}
238 		return text.toString();
239 	}
240 
getLastRefreshEvent()241 	public IRefreshEvent getLastRefreshEvent() {
242 		return lastRefreshEvent;
243 	}
244 
getRefreshIntervalAsString()245 	private String getRefreshIntervalAsString() {
246 		if (runOnce)
247 			return TeamUIMessages.RefreshSchedule_16;
248 		boolean hours = false;
249 		long seconds = getRefreshInterval();
250 		if(seconds <= 60) {
251 			seconds = 60;
252 		}
253 		long minutes = seconds / 60;
254 		if(minutes >= 60) {
255 			minutes = minutes / 60;
256 			hours = true;
257 		}
258 		String unit;
259 		if(minutes >= 1) {
260 			unit = (hours ? TeamUIMessages.RefreshSchedule_9 : TeamUIMessages.RefreshSchedule_10);
261 		} else {
262 			unit = (hours ? TeamUIMessages.RefreshSchedule_11 : TeamUIMessages.RefreshSchedule_12);
263 		}
264 		return NLS.bind(TeamUIMessages.RefreshSchedule_13, new String[] { Long.toString(minutes), unit });
265 	}
266 
getRefreshable()267 	public IRefreshable getRefreshable() {
268 		return refreshable;
269 	}
270 
271 	/**
272 	 * @return The time when the job should start or <code>null</code> when it
273 	 *         should be run immediately.
274 	 */
getRefreshStartTime()275 	public Date getRefreshStartTime() {
276 		return refreshStart;
277 	}
278 
setRefreshStartTime(Date refreshStart)279 	public void setRefreshStartTime(Date refreshStart) {
280 		if(refreshStart==null || refreshStart != getRefreshStartTime()) {
281 			stopJob();
282 			this.refreshStart = refreshStart;
283 			if(isEnabled()) {
284 				startJob();
285 			}
286 		}
287 	}
288 
289 	/**
290 	 * @return Return <code>false</code> if the job should be run again when
291 	 *         finished, or <code>true</code> otherwise.
292 	 */
getRunOnce()293 	public boolean getRunOnce() {
294 		return runOnce;
295 	}
296 
setRunOnce(boolean runOnce)297 	public void setRunOnce(boolean runOnce) {
298 		if (runOnce != getRunOnce()) {
299 			stopJob();
300 			this.runOnce = runOnce;
301 			if (isEnabled()) {
302 				startJob();
303 			}
304 		}
305 	}
306 }
307