/******************************************************************************* * Copyright (c) 2003, 2015 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation * Philipp Bumann <bumannp@gmail.com> - Bug 477602 *******************************************************************************/ package org.eclipse.e4.ui.progress.internal; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Singleton; import org.eclipse.core.commands.common.EventManager; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.e4.core.di.annotations.Creatable; import org.eclipse.e4.ui.model.application.MApplication; import org.eclipse.e4.ui.progress.IDisposableAction; import org.eclipse.e4.ui.progress.IProgressConstants; /** * This singleton remembers all JobTreeElements that should be preserved (e.g. * because their associated Jobs have the "keep" property set). */ @Creatable @Singleton public class FinishedJobs extends EventManager { /* * Interface for notify listeners. */ static interface KeptJobsListener { /** * A job to be kept has finished * * @param jte */ void finished(JobTreeElement jte); /** * A kept job has been removed. * * @param jte */ void removed(JobTreeElement jte); } private IJobProgressManagerListener listener; private HashSet<JobTreeElement> keptjobinfos = new HashSet<>(); private HashMap<Object, Long> finishedTime = new HashMap<>(); private static JobTreeElement[] EMPTY_INFOS; @Inject ProgressManager progressManager; @PostConstruct void init(MApplication application) { progressManager.addListener(listener); EMPTY_INFOS = new JobTreeElement[0]; // TODO E4 workaround for @creatable problem application.getContext().set(FinishedJobs.class, this); } public FinishedJobs() { listener = new IJobProgressManagerListener() { @Override public void addJob(JobInfo info) { checkForDuplicates(info); } @Override public void addGroup(GroupInfo info) { checkForDuplicates(info); } @Override public void refreshJobInfo(JobInfo info) { checkTasks(info); } @Override public void refreshGroup(GroupInfo info) { } @Override public void refreshAll() { } @Override public void removeJob(JobInfo info) { if (keep(info)) { checkForDuplicates(info); add(info); } } @Override public void removeGroup(GroupInfo group) { } @Override public boolean showsDebug() { return false; } }; } /** * Returns true if JobInfo indicates that it must be kept. */ static boolean keep(JobInfo info) { Job job = info.getJob(); if (job != null) { Object prop = job.getProperty(ProgressManagerUtil.KEEP_PROPERTY); if (prop instanceof Boolean) { if (((Boolean) prop).booleanValue()) { return true; } } prop = job.getProperty(ProgressManagerUtil.KEEPONE_PROPERTY); if (prop instanceof Boolean) { if (((Boolean) prop).booleanValue()) { return true; } } IStatus status = job.getResult(); if (status != null && status.getSeverity() == IStatus.ERROR) { return true; } } return false; } /** * Register for notification. */ void addListener(KeptJobsListener l) { addListenerObject(l); } /** * Deregister for notification. */ void removeListener(KeptJobsListener l) { removeListenerObject(l); } private void checkForDuplicates(GroupInfo info) { Object[] objects = info.getChildren(); for (Object object : objects) { if (object instanceof JobInfo) { checkForDuplicates((JobInfo) object); } } } private void checkForDuplicates(JobTreeElement info) { JobTreeElement[] toBeRemoved = findJobsToRemove(info); if (toBeRemoved != null) { for (JobTreeElement element : toBeRemoved) { remove(element); } } } /** * Add given Job to list of kept jobs. */ private void add(JobInfo info) { boolean fire = false; synchronized (keptjobinfos) { if (!keptjobinfos.contains(info)) { keptjobinfos.add(info); long now = System.currentTimeMillis(); finishedTime.put(info, new Long(now)); GroupInfo parent = info.getParent(); if (!(parent == null || keptjobinfos.contains(parent))) { keptjobinfos.add(parent); finishedTime.put(parent, new Long(now)); } fire = true; } } if (fire) { Object l[] = getListeners(); for (Object element : l) { KeptJobsListener jv = (KeptJobsListener) element; jv.finished(info); } } } static void disposeAction(JobTreeElement jte) { if (jte.isJobInfo()) { JobInfo ji = (JobInfo) jte; Job job = ji.getJob(); if (job != null) { Object prop = job .getProperty(IProgressConstants.ACTION_PROPERTY); if (prop instanceof IDisposableAction) { ((IDisposableAction) prop).dispose(); } } } } private JobTreeElement[] findJobsToRemove(JobTreeElement info) { if (info.isJobInfo()) { Job myJob = ((JobInfo) info).getJob(); if (myJob != null) { Object prop = myJob .getProperty(ProgressManagerUtil.KEEPONE_PROPERTY); if (prop instanceof Boolean && ((Boolean) prop).booleanValue()) { ArrayList<JobTreeElement> found = null; JobTreeElement[] all; synchronized (keptjobinfos) { all = keptjobinfos .toArray(new JobTreeElement[keptjobinfos.size()]); } for (JobTreeElement jte : all) { if (jte != info && jte.isJobInfo()) { Job job = ((JobInfo) jte).getJob(); if (job != null && job != myJob && job.belongsTo(myJob)) { if (found == null) { found = new ArrayList<>(); } found.add(jte); } } } if (found != null) { return found .toArray(new JobTreeElement[found.size()]); } } } } return null; } private void checkTasks(JobInfo info) { if (keep(info)) { TaskInfo tinfo = info.getTaskInfo(); if (tinfo != null) { JobTreeElement[] toBeRemoved = null; boolean fire = false; JobTreeElement element = (JobTreeElement) tinfo.getParent(); synchronized (keptjobinfos) { if (element == info && !keptjobinfos.contains(tinfo)) { toBeRemoved = findJobsToRemove(element); keptjobinfos.add(tinfo); finishedTime.put(tinfo, new Long(System .currentTimeMillis())); } } if (toBeRemoved != null) { for (JobTreeElement jobTreeElement : toBeRemoved) { remove(jobTreeElement); } } if (fire) { for (Object listener : getListeners()) { KeptJobsListener jv = (KeptJobsListener) listener; jv.finished(info); } } } } } public void removeErrorJobs() { JobTreeElement[] infos = getKeptElements(); for (JobTreeElement info : infos) { if (info.isJobInfo()) { JobInfo info1 = (JobInfo) info; Job job = info1.getJob(); if (job != null) { IStatus status = job.getResult(); if (status != null && status.getSeverity() == IStatus.ERROR) { JobTreeElement topElement = info1.getParent(); if (topElement == null) { topElement = info1; } remove(topElement); } } } } } boolean remove(JobTreeElement jte) { boolean fire = false; boolean removed = false; synchronized (keptjobinfos) { if (keptjobinfos.remove(jte)) { removed = true; finishedTime.remove(jte); disposeAction(jte); // delete all elements that have jte as their direct or indirect // parent JobTreeElement jobTreeElements[] = keptjobinfos .toArray(new JobTreeElement[keptjobinfos.size()]); for (JobTreeElement jobTreeElement : jobTreeElements) { JobTreeElement parent = (JobTreeElement) jobTreeElement .getParent(); if (parent != null) { if (parent == jte || parent.getParent() == jte) { if (keptjobinfos.remove(jobTreeElement)) { disposeAction(jobTreeElement); } finishedTime.remove(jobTreeElement); } } } fire = true; } } if (fire) { // notify listeners Object l[] = getListeners(); for (Object element : l) { KeptJobsListener jv = (KeptJobsListener) element; jv.removed(jte); } } return removed; } /** * Returns all kept elements. */ JobTreeElement[] getKeptElements() { JobTreeElement[] all; if (keptjobinfos.isEmpty()) { return EMPTY_INFOS; } synchronized (keptjobinfos) { all = keptjobinfos .toArray(new JobTreeElement[keptjobinfos.size()]); } return all; } /** * Get the date that indicates the finish time. * * @param jte * @return Date */ public Date getFinishDate(JobTreeElement jte) { Object o = finishedTime.get(jte); if (o instanceof Long) { return new Date(((Long) o).longValue()); } return null; } /** * Return whether or not the kept infos have the element. * * @param element * @return boolean */ public boolean isKept(JobTreeElement element) { return keptjobinfos.contains(element); } /** * Clear all kept jobs. */ public void clearAll() { synchronized (keptjobinfos) { JobTreeElement[] all = keptjobinfos .toArray(new JobTreeElement[keptjobinfos.size()]); for (JobTreeElement element : all) { disposeAction(element); } keptjobinfos.clear(); finishedTime.clear(); } // notify listeners Object l[] = getListeners(); for (Object element : l) { KeptJobsListener jv = (KeptJobsListener) element; jv.removed(null); } } /** * Return the set of kept jobs. * @return Set */ Set<JobTreeElement> getKeptAsSet() { return keptjobinfos; } }