KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > debug > internal > ui > launchConfigurations > LaunchConfigurationManager


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.debug.internal.ui.launchConfigurations;
12
13 import java.io.BufferedInputStream JavaDoc;
14 import java.io.File JavaDoc;
15 import java.io.FileInputStream JavaDoc;
16 import java.io.FileOutputStream JavaDoc;
17 import java.io.IOException JavaDoc;
18 import java.io.InputStream JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.Collection JavaDoc;
21 import java.util.Collections JavaDoc;
22 import java.util.Comparator JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.Set JavaDoc;
29
30 import javax.xml.parsers.DocumentBuilder JavaDoc;
31 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
32 import javax.xml.parsers.ParserConfigurationException JavaDoc;
33 import javax.xml.transform.TransformerException JavaDoc;
34
35 import org.eclipse.core.expressions.EvaluationContext;
36 import org.eclipse.core.expressions.IEvaluationContext;
37 import org.eclipse.core.resources.IContainer;
38 import org.eclipse.core.resources.IFile;
39 import org.eclipse.core.resources.IResource;
40 import org.eclipse.core.resources.ISaveContext;
41 import org.eclipse.core.resources.ISaveParticipant;
42 import org.eclipse.core.runtime.CoreException;
43 import org.eclipse.core.runtime.IConfigurationElement;
44 import org.eclipse.core.runtime.IExtensionPoint;
45 import org.eclipse.core.runtime.IPath;
46 import org.eclipse.core.runtime.IStatus;
47 import org.eclipse.core.runtime.Platform;
48 import org.eclipse.core.runtime.Status;
49 import org.eclipse.debug.core.DebugPlugin;
50 import org.eclipse.debug.core.ILaunch;
51 import org.eclipse.debug.core.ILaunchConfiguration;
52 import org.eclipse.debug.core.ILaunchConfigurationType;
53 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
54 import org.eclipse.debug.core.ILaunchDelegate;
55 import org.eclipse.debug.core.ILaunchListener;
56 import org.eclipse.debug.core.ILaunchManager;
57 import org.eclipse.debug.core.ILaunchMode;
58 import org.eclipse.debug.internal.core.IConfigurationElementConstants;
59 import org.eclipse.debug.internal.core.LaunchManager;
60 import org.eclipse.debug.internal.ui.DebugPluginImages;
61 import org.eclipse.debug.internal.ui.DebugUIPlugin;
62 import org.eclipse.debug.internal.ui.IInternalDebugUIConstants;
63 import org.eclipse.debug.internal.ui.ILaunchHistoryChangedListener;
64 import org.eclipse.debug.ui.DebugUITools;
65 import org.eclipse.debug.ui.IDebugUIConstants;
66 import org.eclipse.debug.ui.ILaunchConfigurationTab;
67 import org.eclipse.debug.ui.ILaunchGroup;
68 import org.eclipse.jface.resource.ImageRegistry;
69 import org.eclipse.swt.SWT;
70 import org.eclipse.swt.graphics.Image;
71 import org.eclipse.swt.widgets.Display;
72 import org.eclipse.ui.IFileEditorInput;
73 import org.eclipse.ui.PlatformUI;
74 import org.eclipse.ui.activities.IWorkbenchActivitySupport;
75 import org.eclipse.ui.activities.WorkbenchActivityHelper;
76 import org.w3c.dom.Document JavaDoc;
77 import org.w3c.dom.Element JavaDoc;
78 import org.w3c.dom.Node JavaDoc;
79 import org.w3c.dom.NodeList JavaDoc;
80 import org.xml.sax.InputSource JavaDoc;
81 import org.xml.sax.SAXException JavaDoc;
82 import org.xml.sax.helpers.DefaultHandler JavaDoc;
83
84 /**
85  * Manages UI related launch configuration artifacts
86  *
87  * Since 3.3 the Launch Configuration Manager is an <code>ISaveParticipant</code>, allowing it to participate in
88  * workspace persistence life-cycles.
89  *
90  * @see ISaveParticipant
91  * @see org.eclipse.debug.ui.ILaunchShortcut
92  * @see ILaunchGroup
93  * @see ILaunchListener
94  * @see ILaunchHistoryChangedListener
95  * @see DebugUIPlugin
96  * @see LaunchHistory
97  */

98 public class LaunchConfigurationManager implements ILaunchListener, ISaveParticipant {
99     /**
100      * Launch group extensions, keyed by launch group identifier.
101      */

102     protected Map JavaDoc fLaunchGroups;
103     
104     /**
105      * Launch histories keyed by launch group identifier
106      */

107     protected Map JavaDoc fLaunchHistories;
108         
109     /**
110      * The list of registered implementors of <code>ILaunchHistoryChangedListener</code>
111      */

112     protected List JavaDoc fLaunchHistoryChangedListeners = new ArrayList JavaDoc(3);
113
114     /**
115      * Launch shortcuts
116      */

117     private List JavaDoc fLaunchShortcuts = null;
118     
119     /**
120      * Launch shortcuts, cached by perspective ids
121      */

122     private Map JavaDoc fLaunchShortcutsByPerspective = null;
123         
124     /**
125      * Cache of launch configuration tab images with error overlays
126      */

127     protected ImageRegistry fErrorImages = null;
128     
129     /**
130      * true when restoring launch history
131      */

132     protected boolean fRestoring = false;
133     
134     /**
135      * A set containing the launch modes supported by
136      * current configurations.
137      */

138     private Set JavaDoc fLoadedModes = null;
139         
140     /**
141      * The name of the file used to persist the launch history.
142      */

143     private static final String JavaDoc LAUNCH_CONFIGURATION_HISTORY_FILENAME = "launchConfigurationHistory.xml"; //$NON-NLS-1$
144

145     /**
146      * performs initialization of the manager when it is started
147      */

148     public void startup() {
149         ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
150         launchManager.addLaunchListener(this);
151         DebugUIPlugin.getDefault().addSaveParticipant(this);
152         //update histories for launches already registered
153
ILaunch[] launches = launchManager.getLaunches();
154         for (int i = 0; i < launches.length; i++) {
155             launchAdded(launches[i]);
156         }
157     }
158     
159     /**
160      * Returns whether any launch config supports the given mode.
161      *
162      * @param mode launch mode
163      * @return whether any launch config supports the given mode
164      */

165     public boolean launchModeAvailable(String JavaDoc mode) {
166         if (fLoadedModes == null) {
167             ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
168             ILaunchConfigurationType[] types = launchManager.getLaunchConfigurationTypes();
169             ILaunchMode[] modes = launchManager.getLaunchModes();
170             fLoadedModes = new HashSet JavaDoc(3);
171             for (int i = 0; i < types.length; i++) {
172                 for (int j = 0; j < modes.length; j++) {
173                     if (types[i].supportsMode(modes[j].getIdentifier())) {
174                         fLoadedModes.add(modes[j].getIdentifier());
175                     }
176                 }
177             }
178         }
179         return fLoadedModes.contains(mode);
180     }
181     
182     /**
183      * Returns whether the given launch configuration should be visible in the
184      * debug ui. If the config is marked as private, or belongs to a different
185      * category (i.e. non-null), then this configuration should not be displayed
186      * in the debug ui.
187      *
188      * @param launchConfiguration
189      * @return boolean
190      */

191     public static boolean isVisible(ILaunchConfiguration launchConfiguration) {
192         try {
193             return !(launchConfiguration.getAttribute(IDebugUIConstants.ATTR_PRIVATE, false));
194         } catch (CoreException e) {
195         }
196         return false;
197     }
198     
199     /**
200      * Returns a collection of launch configurations that does not contain
201      * configurations from disabled activities.
202      *
203      * @param configurations a collection of configurations
204      * @return the given collection minus any configurations from disabled activities
205      */

206     public static ILaunchConfiguration[] filterConfigs(ILaunchConfiguration[] configurations) {
207         IWorkbenchActivitySupport activitySupport = PlatformUI.getWorkbench().getActivitySupport();
208         if (activitySupport == null) {
209             return configurations;
210         }
211         List JavaDoc filteredConfigs = new ArrayList JavaDoc();
212         ILaunchConfigurationType type = null;
213         LaunchConfigurationTypeContribution contribution = null;
214         ILaunchConfiguration configuration = null;
215         for (int i = 0; i < configurations.length; i++) {
216             configuration = configurations[i];
217             try {
218                 type = configuration.getType();
219                 contribution = new LaunchConfigurationTypeContribution(type);
220                 if (DebugUIPlugin.doLaunchConfigurationFiltering(configuration) & !WorkbenchActivityHelper.filterItem(contribution)) {
221                     filteredConfigs.add(configuration);
222                 }
223             }
224             catch (CoreException e) {DebugUIPlugin.log(e.getStatus());}
225         }
226         return (ILaunchConfiguration[]) filteredConfigs.toArray(new ILaunchConfiguration[filteredConfigs.size()]);
227     }
228
229     /**
230      * Returns a listing of <code>IlaunchDeleagtes</code> that does not contain any delegates from disabled activities
231      * @param delegates the raw listing of delegates to filter
232      * @return the filtered listing of <code>ILaunchDelegate</code>s or an empty array, never <code>null</code>.
233      * @since 3.3
234      */

235     public static ILaunchDelegate[] filterLaunchDelegates(ILaunchConfigurationType type, Set JavaDoc modes) throws CoreException {
236         IWorkbenchActivitySupport as = PlatformUI.getWorkbench().getActivitySupport();
237         ILaunchDelegate[] delegates = type.getDelegates(modes);
238         if(as == null) {
239             return delegates;
240         }
241         HashSet JavaDoc set = new HashSet JavaDoc();
242         for(int i = 0; i < delegates.length; i++) {
243             //filter by capabilities
244
if(!WorkbenchActivityHelper.filterItem(new LaunchDelegateContribution(delegates[i]))) {
245                 set.add(delegates[i]);
246             }
247         }
248         return (ILaunchDelegate[]) set.toArray(new ILaunchDelegate[set.size()]);
249     }
250     
251     /**
252      * Performs cleanup operations when the manager is being disposed of.
253      */

254     public void shutdown() {
255         ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
256         launchManager.removeLaunchListener(this);
257         if (fLaunchHistories != null) {
258             Iterator JavaDoc histories = fLaunchHistories.values().iterator();
259             while (histories.hasNext()) {
260                 LaunchHistory history = (LaunchHistory)histories.next();
261                 history.dispose();
262             }
263         }
264         DebugUIPlugin.getDefault().removeSaveParticipant(this);
265     }
266     
267     /**
268      * @see ILaunchListener#launchRemoved(ILaunch)
269      */

270     public void launchRemoved(ILaunch launch) {}
271     
272     /**
273      * @see ILaunchListener#launchChanged(ILaunch)
274      */

275     public void launchChanged(ILaunch launch) {}
276
277     /**
278      * Must not assume that will only be called from the UI thread.
279      *
280      * @see ILaunchListener#launchAdded(ILaunch)
281      */

282     public void launchAdded(final ILaunch launch) {
283         removeTerminatedLaunches(launch);
284     }
285     
286     /**
287      * Removes terminated launches from the launch view, leaving the specified launch in the view
288      * @param newLaunch the newly added launch to leave in the view
289      */

290     protected void removeTerminatedLaunches(ILaunch newLaunch) {
291         if (DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugUIConstants.PREF_AUTO_REMOVE_OLD_LAUNCHES)) {
292             ILaunchManager lManager= DebugPlugin.getDefault().getLaunchManager();
293             Object JavaDoc[] launches= lManager.getLaunches();
294             for (int i= 0; i < launches.length; i++) {
295                 ILaunch launch= (ILaunch)launches[i];
296                 if (launch != newLaunch && launch.isTerminated()) {
297                     lManager.removeLaunch(launch);
298                 }
299             }
300         }
301     }
302     
303     /**
304      * Returns the most recent launch for the given group, or <code>null</code>
305      * if none. This method does not include any filtering for the returned launch configuration.
306      *
307      * This method is exposed via DebugTools.getLastLaunch
308      *
309      * @return the last launch, or <code>null</code> if none
310      */

311     public ILaunchConfiguration getLastLaunch(String JavaDoc groupId) {
312         LaunchHistory history = getLaunchHistory(groupId);
313         if (history != null) {
314             return history.getRecentLaunch();
315         }
316         return null;
317     }
318     
319     /**
320      * Returns the most recent launch for the given group taking launch configuration
321      * filters into account, or <code>null</code> if none.
322      *
323      * @param groupId launch group
324      * @return the most recent, un-filtered launch
325      */

326     public ILaunchConfiguration getFilteredLastLaunch(String JavaDoc groupId) {
327         LaunchHistory history = getLaunchHistory(groupId);
328         if (history != null) {
329             ILaunchConfiguration[] filterConfigs = history.getCompleteLaunchHistory();
330             if (filterConfigs.length > 0) {
331                 return filterConfigs[0];
332             }
333         }
334         return null;
335     }
336     
337     /**
338      * Add the specified listener to the list of listeners that will be notified when the
339      * launch history changes.
340      */

341     public void addLaunchHistoryListener(ILaunchHistoryChangedListener listener) {
342         if (!fLaunchHistoryChangedListeners.contains(listener)) {
343             fLaunchHistoryChangedListeners.add(listener);
344         }
345     }
346     
347     /**
348      * Remove the specified listener from the list of listeners that will be notified when the
349      * launch history changes.
350      */

351     public void removeLaunchHistoryListener(ILaunchHistoryChangedListener listener) {
352         fLaunchHistoryChangedListeners.remove(listener);
353     }
354     
355     /**
356      * Notify all launch history listeners that the launch history has changed in some way.
357      */

358     protected void fireLaunchHistoryChanged() {
359         Iterator JavaDoc iterator = fLaunchHistoryChangedListeners.iterator();
360         ILaunchHistoryChangedListener listener = null;
361         while (iterator.hasNext()) {
362             listener = (ILaunchHistoryChangedListener) iterator.next();
363             listener.launchHistoryChanged();
364         }
365     }
366
367     /**
368      * Returns the history listing as XML
369      * @return the history listing as XML
370      * @throws CoreException
371      * @throws ParserConfigurationException
372      * @throws TransformerException
373      * @throws IOException
374      */

375     protected String JavaDoc getHistoryAsXML() throws CoreException, ParserConfigurationException JavaDoc, TransformerException JavaDoc, IOException JavaDoc {
376         Document JavaDoc doc = DebugUIPlugin.getDocument();
377         Element historyRootElement = doc.createElement(IConfigurationElementConstants.LAUNCH_HISTORY);
378         doc.appendChild(historyRootElement);
379         
380         Iterator JavaDoc histories = fLaunchHistories.values().iterator();
381         LaunchHistory history = null;
382         while (histories.hasNext()) {
383             history = (LaunchHistory)histories.next();
384             Element groupElement = doc.createElement(IConfigurationElementConstants.LAUNCH_GROUP);
385             groupElement.setAttribute(IConfigurationElementConstants.ID, history.getLaunchGroup().getIdentifier());
386             historyRootElement.appendChild(groupElement);
387             Element historyElement = doc.createElement(IConfigurationElementConstants.MRU_HISTORY);
388             groupElement.appendChild(historyElement);
389             createEntry(doc, historyElement, history.getCompleteLaunchHistory());
390             Element favs = doc.createElement(IConfigurationElementConstants.FAVORITES);
391             groupElement.appendChild(favs);
392             createEntry(doc, favs, history.getFavorites());
393             history.setSaved(true);
394         }
395         return DebugUIPlugin.serializeDocument(doc);
396     }
397
398     /**
399      * Creates a new launch history element and adds it to the specified <code>Document</code>
400      * @param doc the <code>Document</code> to add the new element to
401      * @param historyRootElement the root element
402      * @param mode the modes the history element should apply to
403      * @param configurations the configurations to create entries for
404      * @throws CoreException
405      */

406     protected void createEntry(Document JavaDoc doc, Element historyRootElement, ILaunchConfiguration[] configurations) throws CoreException {
407         for (int i = 0; i < configurations.length; i++) {
408             ILaunchConfiguration configuration = configurations[i];
409             if (configuration.exists()) {
410                 Element launch = doc.createElement(IConfigurationElementConstants.LAUNCH);
411                 launch.setAttribute(IConfigurationElementConstants.MEMENTO, configuration.getMemento());
412                 historyRootElement.appendChild(launch);
413             }
414         }
415     }
416                 
417     /**
418      * Returns the path to the local file for the launch history
419      * @return the file path for the launch history file
420      */

421     protected IPath getHistoryFilePath() {
422         return DebugUIPlugin.getDefault().getStateLocation().append(LAUNCH_CONFIGURATION_HISTORY_FILENAME);
423     }
424
425     /**
426      * Write out an XML file indicating the entries on the run & debug history lists and
427      * the most recent launch.
428      */

429     protected void persistLaunchHistory() throws IOException JavaDoc, CoreException, TransformerException JavaDoc, ParserConfigurationException JavaDoc {
430         synchronized (this) {
431             if (fLaunchHistories == null || fRestoring) {
432                 return;
433             }
434         }
435         boolean shouldsave = false;
436         for(Iterator JavaDoc iter = fLaunchHistories.values().iterator(); iter.hasNext();) {
437             shouldsave |= ((LaunchHistory)iter.next()).needsSaving();
438         }
439         if(shouldsave) {
440             IPath historyPath = getHistoryFilePath();
441             String JavaDoc osHistoryPath = historyPath.toOSString();
442             String JavaDoc xml = getHistoryAsXML();
443             File JavaDoc file = new File JavaDoc(osHistoryPath);
444             file.createNewFile();
445             
446             FileOutputStream JavaDoc stream = new FileOutputStream JavaDoc(file);
447             stream.write(xml.getBytes("UTF8")); //$NON-NLS-1$
448
stream.close();
449         }
450     }
451     
452     /**
453      * Find the XML history file and parse it. Place the corresponding configurations
454      * in the appropriate history, and set the most recent launch.
455      */

456     private void restoreLaunchHistory() {
457         // Find the history file
458
IPath historyPath = getHistoryFilePath();
459         String JavaDoc osHistoryPath = historyPath.toOSString();
460         File JavaDoc file = new File JavaDoc(osHistoryPath);
461         // If no history file, nothing to do
462
if (!file.exists()) {
463             return;
464         }
465         InputStream JavaDoc stream= null;
466         Element rootHistoryElement= null;
467         try {
468             // Parse the history file
469
stream = new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(file));
470             try {
471                 DocumentBuilder JavaDoc parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
472                 parser.setErrorHandler(new DefaultHandler JavaDoc());
473                 rootHistoryElement = parser.parse(new InputSource JavaDoc(stream)).getDocumentElement();
474             } catch (SAXException JavaDoc e) {
475                 DebugUIPlugin.log(e);
476                 return;
477             } catch (ParserConfigurationException JavaDoc e) {
478                 DebugUIPlugin.log(e);
479                 return;
480             } finally {
481                 stream.close();
482             }
483         } catch (IOException JavaDoc exception) {
484             DebugUIPlugin.log(exception);
485             return;
486         }
487         // If root node isn't what we expect, return
488
if (!rootHistoryElement.getNodeName().equalsIgnoreCase(IConfigurationElementConstants.LAUNCH_HISTORY)) {
489             return;
490         }
491         // For each child of the root node, construct a launch config handle and add it to
492
// the appropriate history, or set the most recent launch
493
Collection JavaDoc l = fLaunchHistories.values();
494         LaunchHistory[] histories = (LaunchHistory[])l.toArray(new LaunchHistory[l.size()]);
495         NodeList JavaDoc list = rootHistoryElement.getChildNodes();
496         int length = list.getLength();
497         Node JavaDoc node = null;
498         Element entry = null;
499         for (int i = 0; i < length; ++i) {
500             node = list.item(i);
501             short type = node.getNodeType();
502             if (type == Node.ELEMENT_NODE) {
503                 entry = (Element) node;
504                 if (entry.getNodeName().equalsIgnoreCase(IConfigurationElementConstants.LAUNCH)) {
505                     createHistoryElement(entry, histories, false);
506                 } else if (entry.getNodeName().equalsIgnoreCase(IConfigurationElementConstants.LAST_LAUNCH)) {
507                     createHistoryElement(entry, histories, true);
508                 } else if (entry.getNodeName().equals(IConfigurationElementConstants.LAUNCH_GROUP)) {
509                     String JavaDoc id = entry.getAttribute(IConfigurationElementConstants.ID);
510                     if (id != null) {
511                         LaunchHistory history = getLaunchHistory(id);
512                         if (history != null) {
513                             restoreHistory(entry, history);
514                         }
515                     }
516                 }
517             }
518         }
519     }
520     
521     /**
522      * Restores the given launch history.
523      *
524      * @param groupElement launch group history
525      * @param history associated history cache
526      */

527     private void restoreHistory(Element groupElement, LaunchHistory history) {
528         NodeList JavaDoc nodes = groupElement.getChildNodes();
529         int length = nodes.getLength();
530         for (int i = 0; i < length; i++) {
531             Node JavaDoc node = nodes.item(i);
532             if (node.getNodeType() == Node.ELEMENT_NODE) {
533                 Element element = (Element)node;
534                 if (element.getNodeName().equals(IConfigurationElementConstants.MRU_HISTORY)) {
535                     ILaunchConfiguration[] configs = getLaunchConfigurations(element);
536                     for (int j = 0; j < configs.length; j++) {
537                         history.addHistory(configs[j], false);
538                     }
539                 } else if (element.getNodeName().equals(IConfigurationElementConstants.FAVORITES)) {
540                     ILaunchConfiguration[] favs = getLaunchConfigurations(element);
541                     history.setFavorites(favs);
542                 }
543             }
544         }
545     }
546     
547     /**
548      * Restores a list of configurations.
549      * @param root element
550      * @return list of configurations under the element
551      */

552     private ILaunchConfiguration[] getLaunchConfigurations(Element root) {
553         List JavaDoc configs = new ArrayList JavaDoc();
554         NodeList JavaDoc nodes = root.getChildNodes();
555         int length = nodes.getLength();
556         for (int i = 0; i < length; i++) {
557             Node JavaDoc node = nodes.item(i);
558             if (node.getNodeType() == Node.ELEMENT_NODE) {
559                 Element element = (Element) node;
560                 if (element.getNodeName().equals(IConfigurationElementConstants.LAUNCH)) {
561                     String JavaDoc memento = element.getAttribute(IConfigurationElementConstants.MEMENTO);
562                     if (memento != null) {
563                         try {
564                             ILaunchConfiguration configuration = DebugPlugin.getDefault().getLaunchManager().getLaunchConfiguration(memento);
565                             if (configuration.exists()) {
566                                 configs.add(configuration);
567                             }
568                         } catch (CoreException e) {
569                         }
570                     }
571                 }
572             }
573         }
574         return (ILaunchConfiguration[]) configs.toArray(new ILaunchConfiguration[configs.size()]);
575     }
576     
577     /**
578      * Construct a launch configuration corresponding to the specified XML
579      * element, and place it in the appropriate history.
580      */

581     private void createHistoryElement(Element entry, LaunchHistory[] histories, boolean prepend) {
582         String JavaDoc memento = entry.getAttribute(IConfigurationElementConstants.MEMENTO);
583         String JavaDoc mode = entry.getAttribute(IConfigurationElementConstants.MODE);
584         try {
585             ILaunchConfiguration launchConfig = DebugPlugin.getDefault().getLaunchManager().getLaunchConfiguration(memento);
586             if (launchConfig.exists()) {
587                 LaunchHistory history = null;
588                 for (int i = 0; i < histories.length; i++) {
589                     history = histories[i];
590                     if (history.accepts(launchConfig) && history.getLaunchGroup().getMode().equals(mode)) {
591                         history.addHistory(launchConfig, prepend);
592                     }
593                 }
594             }
595         } catch (CoreException e) {
596             //do nothing, as we want to throw away invalid launch history entries silently
597
}
598     }
599     
600     /**
601      * Load all registered extensions of the 'launch shortcut' extension point.
602      */

603     private void loadLaunchShortcuts() {
604         if(fLaunchShortcuts == null) {
605             // Get the configuration elements
606
IExtensionPoint extensionPoint= Platform.getExtensionRegistry().getExtensionPoint(DebugUIPlugin.getUniqueIdentifier(), IDebugUIConstants.EXTENSION_POINT_LAUNCH_SHORTCUTS);
607             IConfigurationElement[] infos= extensionPoint.getConfigurationElements();
608     
609             // Load the configuration elements into a Map
610
fLaunchShortcuts = new ArrayList JavaDoc(infos.length);
611             for (int i = 0; i < infos.length; i++) {
612                 fLaunchShortcuts.add(new LaunchShortcutExtension(infos[i]));
613             }
614             Collections.sort(fLaunchShortcuts, new ShortcutComparator());
615         }
616     }
617     
618     /**
619      * Load all registered extensions of the 'launch groups' extension point.
620      */

621     private void loadLaunchGroups() {
622         if (fLaunchGroups == null) {
623             // Get the configuration elements
624
IExtensionPoint extensionPoint= Platform.getExtensionRegistry().getExtensionPoint(DebugUIPlugin.getUniqueIdentifier(), IDebugUIConstants.EXTENSION_POINT_LAUNCH_GROUPS);
625             IConfigurationElement[] infos= extensionPoint.getConfigurationElements();
626     
627             // Load the configuration elements into a Map
628
fLaunchGroups = new HashMap JavaDoc(infos.length);
629             LaunchGroupExtension ext = null;
630             for (int i = 0; i < infos.length; i++) {
631                 ext = new LaunchGroupExtension(infos[i]);
632                 fLaunchGroups.put(ext.getIdentifier(), ext);
633             }
634         }
635     }
636     
637     /**
638      * Returns all launch shortcuts
639      *
640      * @return all launch shortcuts
641      */

642     public List JavaDoc getLaunchShortcuts() {
643         if (fLaunchShortcuts == null) {
644             loadLaunchShortcuts();
645         }
646         return fLaunchShortcuts;
647     }
648
649     /**
650      * Creates a listing of the launch shortcut extensions that are applicable to the underlying resource
651      * @param resource the underlying resource
652      * @return a listing of applicable launch shortcuts or an empty list, never <code>null</code>
653      * @since 3.3
654      */

655     public List JavaDoc getLaunchShortcuts(IResource resource) {
656         List JavaDoc list = new ArrayList JavaDoc();
657         List JavaDoc sc = getLaunchShortcuts();
658         List JavaDoc ctxt = new ArrayList JavaDoc();
659         ctxt.add(resource);
660         IEvaluationContext context = new EvaluationContext(null, ctxt);
661         context.addVariable("selection", ctxt); //$NON-NLS-1$
662
LaunchShortcutExtension ext = null;
663         for(Iterator JavaDoc iter = sc.iterator(); iter.hasNext();) {
664             ext = (LaunchShortcutExtension) iter.next();
665             try {
666                 if(ext.evalEnablementExpression(context, ext.getContextualLaunchEnablementExpression()) && !WorkbenchActivityHelper.filterItem(ext)) {
667                     if(!list.contains(ext)) {
668                         list.add(ext);
669                     }
670                 }
671             }
672             catch(CoreException ce) {/*do nothing*/}
673         }
674         return list;
675     }
676     
677     /**
678      * Returns a list of launch shortcuts based on the given shortcuts that support
679      * the given launch mode.
680      *
681      * @param shortctus launch shortcuts
682      * @param mode launch mode id
683      * @return shortcuts supporting the given mode, possible an empty list
684      * @since 3.3
685      */

686     public List JavaDoc getShortcutsSupportingMode(List JavaDoc shortctus, String JavaDoc mode) {
687         ArrayList JavaDoc supporting = new ArrayList JavaDoc(shortctus);
688         Iterator JavaDoc iterator = supporting.listIterator();
689         while (iterator.hasNext()) {
690             LaunchShortcutExtension ext = (LaunchShortcutExtension) iterator.next();
691             if (!ext.getModes().contains(mode)) {
692                 iterator.remove();
693             }
694         }
695         return supporting;
696     }
697     
698     /**
699      * Returns a listing of all of the <code>ILaunchConfigurationType</code>s that apply to the currently
700      * specified <code>IResource</code>.
701      *
702      * @param resource the resource context
703      * @return a listing of applicable <code>ILaunchConfigurationType</code>s, or an empty list, never <code>null</code>
704      * @since 3.3
705      * CONTEXTLAUNCHING
706      */

707     public List JavaDoc getApplicableConfigurationTypes(IResource resource) {
708         List JavaDoc types = new ArrayList JavaDoc();
709         List JavaDoc exts = getLaunchShortcuts();
710         LaunchShortcutExtension ext = null;
711         List JavaDoc list = new ArrayList JavaDoc();
712         list.add(resource);
713         IEvaluationContext context = new EvaluationContext(null, list);
714         context.setAllowPluginActivation(true);
715         context.addVariable("selection", list); //$NON-NLS-1$
716
HashSet JavaDoc set = new HashSet JavaDoc();
717         for(Iterator JavaDoc iter = exts.listIterator(); iter.hasNext();) {
718             ext = (LaunchShortcutExtension) iter.next();
719             try {
720                 if(ext.evalEnablementExpression(context, ext.getContextualLaunchEnablementExpression())) {
721                     set.addAll(ext.getAssociatedConfigurationTypes());
722                 }
723             }
724             catch(CoreException ce) {
725                 IStatus status = new Status(IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(), "Launch shortcut '" + ext.getId() + "' enablement expression caused exception. Shortcut was removed.", ce); //$NON-NLS-1$ //$NON-NLS-2$
726
DebugUIPlugin.log(status);
727                 iter.remove();
728             }
729         }
730         LaunchManager lm = (LaunchManager) DebugPlugin.getDefault().getLaunchManager();
731         ILaunchConfigurationType type = null;
732         for(Iterator JavaDoc iter = set.iterator(); iter.hasNext();) {
733             type = lm.getLaunchConfigurationType((String JavaDoc)iter.next());
734             if(type != null) {
735                 if(!types.contains(type) && type.isPublic() && !"org.eclipse.ui.externaltools.builder".equals(type.getCategory())) { //$NON-NLS-1$
736
types.add(type);
737                 }
738             }
739         }
740         return types;
741     }
742     
743     /**
744      * Returns a list of the <code>ILaunchConfiguration</code>s that apply to the specified <code>IResource</code>
745      * @param resource the resource
746      * @return a listing of applicable <code>ILaunchConfiguration</code>s for the specified <code>IResource</code> or an empty
747      * list if none, never <code>null</code>
748      * @since 3.3
749      */

750     public List JavaDoc getApplicableLaunchConfigurations(IResource resource) {
751         ArrayList JavaDoc list = new ArrayList JavaDoc();
752         try {
753             List JavaDoc types = getApplicableConfigurationTypes(resource);
754             ILaunchConfiguration[] configurations = filterConfigs(getLaunchManager().getLaunchConfigurations());
755             ILaunchConfiguration configuration = null;
756             IResource[] resources = null;
757             for(int i = 0; i < configurations.length; i++) {
758                 configuration = configurations[i];
759                 if(types.contains(configuration.getType()) && acceptConfiguration(configuration)) {
760                     resources = configuration.getMappedResources();
761                     if (resources != null) {
762                         for (int j = 0; j < resources.length; j++) {
763                             if (resource.equals(resources[j]) || resource.getFullPath().isPrefixOf(resources[j].getFullPath())) {
764                                 list.add(configuration);
765                                 break;
766                             }
767                         }
768                     }
769                     else {
770                         //in the event the config has no mapping
771
list.add(configuration);
772                     }
773                 }
774             }
775         } catch (CoreException e) {
776             list.clear();
777             DebugPlugin.log(e);
778         }
779         return list;
780     }
781     
782     /**
783      * Returns if the specified configuration should be considered as a potential candidate
784      * @param config
785      * @return if the specified configuration should be considered as a potential candidate
786      * @throws CoreException
787      */

788     private boolean acceptConfiguration(ILaunchConfiguration config) throws CoreException {
789         if(config != null && !DebugUITools.isPrivate(config)) {
790             if(!"org.eclipse.ui.externaltools".equals(config.getType().getCategory())) { //$NON-NLS-1$
791
return true;
792             }
793             else {
794                 IResource[] res = config.getMappedResources();
795                 if(res != null) {
796                     return true;
797                 }
798             }
799         }
800         return false;
801     }
802     
803     /**
804      * Returns all launch shortcuts for the given category
805      *
806      * @return all launch shortcuts
807      */

808     public List JavaDoc getLaunchShortcuts(String JavaDoc category) {
809         return filterShortcuts(getLaunchShortcuts(), category);
810     }
811     
812     /**
813      * Return a list of filtered launch shortcuts, based on the given category.
814      *
815      * @param unfiltered
816      * @param category
817      * @return List
818      */

819     protected List JavaDoc filterShortcuts(List JavaDoc unfiltered, String JavaDoc category) {
820         List JavaDoc filtered = new ArrayList JavaDoc(unfiltered.size());
821         Iterator JavaDoc iter = unfiltered.iterator();
822         LaunchShortcutExtension extension = null;
823         while (iter.hasNext()){
824             extension = (LaunchShortcutExtension)iter.next();
825             if (category == null) {
826                 if (extension.getCategory() == null) {
827                     filtered.add(extension);
828                 }
829             } else if (category.equals(extension.getCategory())){
830                 filtered.add(extension);
831             }
832         }
833         return filtered;
834     }
835     
836     /**
837      * Returns all launch shortcuts defined for the given perspective,
838      * empty list if none.
839      *
840      * @param perpsective perspective identifier
841      * @param category the category for the shortcut
842      * @return all launch shortcuts defined for the given perspective,
843      * empty list if none.
844      * @deprecated the use of perspectives for launch shortcuts has been
845      * deprecated since 3.1, use a contextualLaunch element instead
846      */

847     public List JavaDoc getLaunchShortcuts(String JavaDoc perpsective, String JavaDoc category) {
848         if (fLaunchShortcutsByPerspective == null) {
849             Iterator JavaDoc shortcuts = getLaunchShortcuts().iterator();
850             fLaunchShortcutsByPerspective = new HashMap JavaDoc(10);
851             LaunchShortcutExtension ext = null;
852             Iterator JavaDoc perspectives = null;
853             while (shortcuts.hasNext()) {
854                 ext = (LaunchShortcutExtension)shortcuts.next();
855                 perspectives = ext.getPerspectives().iterator();
856                 while (perspectives.hasNext()) {
857                     String JavaDoc id = (String JavaDoc)perspectives.next();
858                     List JavaDoc list = (List JavaDoc)fLaunchShortcutsByPerspective.get(id);
859                     if (list == null) {
860                         list = new ArrayList JavaDoc(4);
861                         fLaunchShortcutsByPerspective.put(id, list);
862                     }
863                     list.add(ext);
864                 }
865             }
866         }
867         List JavaDoc list = (List JavaDoc)fLaunchShortcutsByPerspective.get(perpsective);
868         if (list == null) {
869             return new ArrayList JavaDoc();
870         }
871         return filterShortcuts(list, category);
872     }
873     
874     /**
875      * Returns the first occurance of any one of the configurations in the provided list, if they are found in the launch history
876      * for the corresponding launch group
877      * @param configurations
878      * @param mode
879      * @param resource
880      * @return the associated launch configuration from the MRU listing or <code>null</code> if there isn't one
881      * @since 3.3
882      */

883     public ILaunchConfiguration getMRUConfiguration(List JavaDoc configurations, ILaunchGroup group, IResource resource) {
884         if(group != null) {
885             ArrayList JavaDoc candidates = new ArrayList JavaDoc();
886             LaunchHistory history = getLaunchHistory(group.getIdentifier());
887             if(history != null) {
888                 ILaunchConfiguration[] configs = history.getCompleteLaunchHistory();
889                 for(int i = 0; i < configs.length; i++) {
890                     if(configurations.contains(configs[i])) {
891                         if(resource instanceof IContainer) {
892                             return configs[i];
893                         }
894                         else {
895                             candidates.add(configs[i]);
896                         }
897                     }
898                 }
899             }
900             //first try to find a config that exactly matches the resource mapping, and collect partial matches
901
ILaunchConfiguration config = null;
902             IResource[] res = null;
903             for(Iterator JavaDoc iter = candidates.iterator(); iter.hasNext();) {
904                 config = (ILaunchConfiguration) iter.next();
905                 try {
906                     res = config.getMappedResources();
907                     if(res != null) {
908                         for(int i = 0; i < res.length; i++) {
909                             if(res[i].equals(resource)) {
910                                 return config;
911                             }
912                         }
913                     }
914                 }
915                 catch(CoreException ce) {}
916             }
917             config = getLastLaunch(group.getIdentifier());
918             if(candidates.contains(config)) {
919                 return config;
920             }
921         }
922         return null;
923     }
924     
925     /**
926      * Returns the <code>LaunchShortcutExtension</code> that has the specified id
927      * or <code>null</code>.
928      * @param id the id of the <code>LaunchShortcutExtension</code> to look for
929      * @return the <code>LaunchShortcutExtension</code> that has the specified id
930      * or <code>null</code>
931      *
932      * @since 3.3
933      */

934     public LaunchShortcutExtension getLaunchShortcut(String JavaDoc id) {
935         loadLaunchShortcuts();
936         LaunchShortcutExtension ext = null;
937         for(int i = 0; i < fLaunchShortcuts.size(); i++) {
938             ext = (LaunchShortcutExtension) fLaunchShortcuts.get(i);
939             if(ext.getId().equals(id)) {
940                 return ext;
941             }
942         }
943         return null;
944     }
945     
946     /**
947      * Returns the shared config from the selected resource or <code>null</code> if the selected resources is not a shared config
948      * @param receiver the object to test if it is a shared launch configuration
949      * @return the shared config from the selected resource or <code>null</code> if the selected resources is not a shared config
950      * @since 3.3
951      */

952     public ILaunchConfiguration isSharedConfig(Object JavaDoc receiver) {
953         if(receiver instanceof IFile) {
954             IFile file = (IFile) receiver;
955             String JavaDoc ext = file.getFileExtension();
956             if(ext == null) {
957                 return null;
958             }
959             if(ext.equals("launch")) { //$NON-NLS-1$
960
ILaunchConfiguration config = DebugPlugin.getDefault().getLaunchManager().getLaunchConfiguration(file);
961                 if(config != null && config.exists()) {
962                     return config;
963                 }
964             }
965         }
966         else if(receiver instanceof IFileEditorInput) {
967             IFileEditorInput input = (IFileEditorInput) receiver;
968             return isSharedConfig(input.getFile());
969         }
970         return null;
971     }
972     
973     /**
974      * Returns the image used to display an error in the given tab
975      */

976     public Image getErrorTabImage(ILaunchConfigurationTab tab) {
977         if (fErrorImages == null) {
978             fErrorImages = new ImageRegistry();
979         }
980         String JavaDoc key = tab.getClass().getName();
981         Image image = fErrorImages.get(key);
982         if (image == null) {
983             // create image
984
Image base = tab.getImage();
985             if (base == null) {
986                 base = DebugPluginImages.getImage(IInternalDebugUIConstants.IMG_OVR_TRANSPARENT);
987             }
988             base = new Image(Display.getCurrent(), base, SWT.IMAGE_COPY);
989             LaunchConfigurationTabImageDescriptor desc = new LaunchConfigurationTabImageDescriptor(base, LaunchConfigurationTabImageDescriptor.ERROR);
990             image = desc.createImage();
991             fErrorImages.put(key, image);
992         }
993         return image;
994     }
995     
996     /**
997      * Return the launch group with the given id, or <code>null</code>
998      *
999      * @return the launch group with the given id, or <code>null</code>
1000     */

1001    public LaunchGroupExtension getLaunchGroup(String JavaDoc id) {
1002        if (fLaunchGroups == null) {
1003            loadLaunchGroups();
1004        }
1005        return (LaunchGroupExtension)fLaunchGroups.get(id);
1006    }
1007    
1008    /**
1009     * Return all defined launch groups
1010     *
1011     * @return all defined launch groups
1012     */

1013    public ILaunchGroup[] getLaunchGroups() {
1014        if (fLaunchGroups == null) {
1015            loadLaunchGroups();
1016        }
1017        Collection JavaDoc groups = fLaunchGroups.values();
1018        return (ILaunchGroup[])groups.toArray(new ILaunchGroup[groups.size()]);
1019    }
1020    
1021    /**
1022     * Return the launch history with the given group id, or <code>null</code>
1023     *
1024     * @return the launch history with the given group id, or <code>null</code>
1025     */

1026    public LaunchHistory getLaunchHistory(String JavaDoc id) {
1027        loadLaunchHistories();
1028        return (LaunchHistory)fLaunchHistories.get(id);
1029    }
1030    
1031    /**
1032     * Returns the singleton instance of the launch manager
1033     * @return the singleton instance of the launch manager
1034     * @since 3.3
1035     */

1036    private LaunchManager getLaunchManager() {
1037        return (LaunchManager) DebugPlugin.getDefault().getLaunchManager();
1038    }
1039    
1040    /**
1041     * Restore launch history
1042     */

1043    private synchronized void loadLaunchHistories() {
1044        if (fLaunchHistories == null) {
1045            fRestoring = true;
1046            ILaunchGroup[] groups = getLaunchGroups();
1047            fLaunchHistories = new HashMap JavaDoc(groups.length);
1048            ILaunchGroup extension = null;
1049            for (int i = 0; i < groups.length; i++) {
1050                extension = groups[i];
1051                if (extension.isPublic()) {
1052                    fLaunchHistories.put(extension.getIdentifier(), new LaunchHistory(extension));
1053                }
1054            }
1055            restoreLaunchHistory();
1056            fRestoring = false;
1057        }
1058    }
1059    
1060    /**
1061     * Returns the default launch group for the given mode.
1062     *
1063     * @param mode
1064     * @return launch group
1065     */

1066    public LaunchGroupExtension getDefaultLaunchGroup(String JavaDoc mode) {
1067        if (mode.equals(ILaunchManager.DEBUG_MODE)) {
1068            return getLaunchGroup(IDebugUIConstants.ID_DEBUG_LAUNCH_GROUP);
1069        }
1070        return getLaunchGroup(IDebugUIConstants.ID_RUN_LAUNCH_GROUP);
1071    }
1072    
1073    /**
1074     * Returns the launch group the given launch configuration belongs to, in
1075     * the specified mode, or <code>null</code> if none.
1076     *
1077     * @param configuration
1078     * @param mode
1079     * @return the launch group the given launch configuration belongs to, in
1080     * the specified mode, or <code>null</code> if none
1081     */

1082    public ILaunchGroup getLaunchGroup(ILaunchConfiguration configuration, String JavaDoc mode) {
1083        try {
1084            String JavaDoc category = configuration.getCategory();
1085            ILaunchGroup[] groups = getLaunchGroups();
1086            ILaunchGroup extension = null;
1087            for (int i = 0; i < groups.length; i++) {
1088                extension = groups[i];
1089                if (category == null) {
1090                    if (extension.getCategory() == null && extension.getMode().equals(mode)) {
1091                        return extension;
1092                    }
1093                } else if (category.equals(extension.getCategory())) {
1094                    if (extension.getMode().equals(mode)) {
1095                        return extension;
1096                    }
1097                }
1098            }
1099        }
1100        catch (CoreException e) {DebugUIPlugin.log(e);}
1101        return null;
1102    }
1103
1104    /**
1105     * Returns the private launch configuration used as a placeholder to represent/store
1106     * the information associated with a launch configuration type.
1107     *
1108     * @param type launch configuration type
1109     * @return launch configuration
1110     * @since 3.0
1111     */

1112    public static ILaunchConfiguration getSharedTypeConfig(ILaunchConfigurationType type) throws CoreException {
1113        String JavaDoc id = type.getIdentifier();
1114        String JavaDoc name = id + ".SHARED_INFO"; //$NON-NLS-1$
1115
ILaunchConfiguration shared = null;
1116        ILaunchConfiguration[] configurations = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurations(type);
1117        ILaunchConfiguration configuration = null;
1118        for (int i = 0; i < configurations.length; i++) {
1119            configuration = configurations[i];
1120            if (configuration.getName().equals(name)) {
1121                shared = configuration;
1122                break;
1123            }
1124        }
1125        
1126        if (shared == null) {
1127            // create a new shared config
1128
ILaunchConfigurationWorkingCopy workingCopy;
1129            workingCopy = type.newInstance(null, name);
1130            workingCopy.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
1131            // null entries indicate default settings
1132
// save
1133
shared = workingCopy.doSave();
1134        }
1135        return shared;
1136    }
1137
1138
1139    /**
1140     * @see org.eclipse.core.resources.ISaveParticipant#doneSaving(org.eclipse.core.resources.ISaveContext)
1141     */

1142    public void doneSaving(ISaveContext context) {}
1143
1144    /**
1145     * @see org.eclipse.core.resources.ISaveParticipant#prepareToSave(org.eclipse.core.resources.ISaveContext)
1146     */

1147    public void prepareToSave(ISaveContext context) throws CoreException {}
1148
1149    /**
1150     * @see org.eclipse.core.resources.ISaveParticipant#rollback(org.eclipse.core.resources.ISaveContext)
1151     */

1152    public void rollback(ISaveContext context) {}
1153
1154    /**
1155     * @see org.eclipse.core.resources.ISaveParticipant#saving(org.eclipse.core.resources.ISaveContext)
1156     */

1157    public void saving(ISaveContext context) throws CoreException {
1158        try {
1159            persistLaunchHistory();
1160        }
1161        catch (CoreException e) {DebugUIPlugin.log(e);}
1162        catch (IOException JavaDoc e) {DebugUIPlugin.log(e);}
1163        catch (ParserConfigurationException JavaDoc e) {DebugUIPlugin.log(e);}
1164        catch (TransformerException JavaDoc e) {DebugUIPlugin.log(e);}
1165    }
1166    
1167    /**
1168     * Sets the given launch to be the most recent launch in the launch
1169     * history (for applicable histories).
1170     * <p>
1171     * @param configuration configuration to be set as most recent
1172     * @since 3.3
1173     */

1174    public void setRecentLaunch(ILaunch launch) {
1175        ILaunchGroup[] groups = DebugUITools.getLaunchGroups();
1176        int size = groups.length;
1177        for (int i = 0; i < size; i++) {
1178            String JavaDoc id = groups[i].getIdentifier();
1179            LaunchHistory history = getLaunchHistory(id);
1180            if (history != null)
1181                history.launchAdded(launch);
1182        }
1183    }
1184
1185}
1186
1187/**
1188 * A comparator for the ordering of launch shortcut extensions
1189 * @since 3.3
1190 */

1191class ShortcutComparator implements Comparator JavaDoc {
1192    /**
1193     * @see Comparator#compare(Object, Object)
1194     */

1195    public int compare(Object JavaDoc a, Object JavaDoc b) {
1196        LaunchShortcutExtension shorcutA = (LaunchShortcutExtension)a;
1197        String JavaDoc labelA = shorcutA.getLabel();
1198        String JavaDoc pathA = shorcutA.getMenuPath();
1199        LaunchShortcutExtension shortcutB = (LaunchShortcutExtension)b;
1200        String JavaDoc labelB = shortcutB.getLabel();
1201        String JavaDoc pathB = shortcutB.getMenuPath();
1202        
1203        // group by path, then sort by label
1204
// a null path sorts last (i.e. highest)
1205
if (nullOrEqual(pathA, pathB)) {
1206            // null labels sort last (i.e. highest)
1207
if (labelA == labelB) {
1208                return 0;
1209            }
1210            if (labelA == null) {
1211                return 1;
1212            }
1213            if (labelB == null) {
1214                return -1;
1215            }
1216            return labelA.compareToIgnoreCase(labelB);
1217        }
1218        // compare paths
1219
if (pathA == null) {
1220            return 1;
1221        }
1222        if (pathB == null) {
1223            return -1;
1224        }
1225        return pathA.compareToIgnoreCase(pathB);
1226    }
1227    
1228    private boolean nullOrEqual(String JavaDoc a, String JavaDoc b) {
1229        if (a == null) {
1230            return b == null;
1231        }
1232        return a.equals(b);
1233    }
1234
1235}
1236
Popular Tags