001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.api.debugger;
043:
044: import java.beans.PropertyChangeListener;
045: import java.beans.PropertyChangeSupport;
046: import java.util.ArrayList;
047: import java.util.Arrays;
048: import java.util.HashSet;
049: import java.util.Iterator;
050: import java.util.List;
051: import org.netbeans.spi.debugger.ContextProvider;
052:
053: /** Session visually represents one process or application. It should
054: * be simple bean with properties like process ID, session name, etc.
055: * All other functionality is deleagted to current debugger engine.
056: *
057: * <p><br><table border="1" cellpadding="3" cellspacing="0" width="100%">
058: * <tbody><tr bgcolor="#ccccff">
059: * <td colspan="2"><font size="+2"><b>Description </b></font></td>
060: * </tr><tr><td align="left" valign="top" width="1%"><font size="+1">
061: * <b>Functionality</b></font></td><td>
062: *
063: * <b>Properties:</b>
064: * Session has two standard read only properties - name ({@link #getName}) and
065: * location name ({@link #getLocationName}).
066: *
067: * <br><br>
068: * <b>Management of languages and engines:</b>
069: * Debugger Core supports debugging in different languages. It means that
070: * each session can be debugged using different languages and
071: * {@link org.netbeans.api.debugger.DebuggerEngine}s. Session manages list
072: * of supported languages ({@link #getSupportedLanguages}) and current
073: * language ({@link #getCurrentLanguage}). Current language can be changed
074: * ({@link #setCurrentLanguage}).
075: * Each language corresponds to one
076: * {@link org.netbeans.api.debugger.DebuggerEngine}
077: * ({@link #getEngineForLanguage}). So, the current language
078: * defines current debuggger engine ({@link #getCurrentEngine})
079: *
080: * A support for a new debugger language can be added during a start of
081: * debugging only. See
082: * {@link org.netbeans.api.debugger.DebuggerManager#startDebugging} and
083: * {@link org.netbeans.spi.debugger.DebuggerEngineProvider}
084: *
085: * <br><br>
086: * <b>Support for aditional services:</b>
087: * Session is final class. The standard method how to
088: * extend its functionality is using lookup methods ({@link #lookup} and
089: * {@link #lookupFirst}).
090: * There are two ways how to register some service provider for some
091: * type of Session:
092: * <ul>
093: * <li>Register 'live' instance of service provider during creation of
094: * new instance of Session (see method
095: * {@link org.netbeans.spi.debugger.SessionProvider#getServices}).
096: * </li>
097: * <li>Register service provider in Manifest-inf/debugger/<type ID>
098: * folder. See Debugger SPI for more information about
099: * registration.</li>
100: * </ul>
101: *
102: * <br>
103: * <b>Support for listening:</b>
104: * Session propagates all changes to
105: * {@link java.beans.PropertyChangeListener}.
106: *
107: * <br>
108: * </td></tr><tr><td align="left" valign="top" width="1%"><font size="+1">
109: * <b>Clinents / Providers</b></font></td><td>
110: *
111: * This class is final, so it does not have any external provider.
112: * Debugger Core and UI modules are clients of this class.
113: *
114: * <br>
115: * </td></tr><tr><td align="left" valign="top" width="1%"><font size="+1">
116: * <b>Lifecycle</b></font></td><td>
117: *
118: * A new instance of Session class can be created and registerred to
119: * {@link org.netbeans.api.debugger.DebuggerManager} during the process
120: * of starting of debugging (see
121: * {@link org.netbeans.api.debugger.DebuggerManager#startDebugging}).
122: *
123: * Session is removed automatically from
124: * {@link org.netbeans.api.debugger.DebuggerManager} when the
125: * number of "supported languages" ({@link #getSupportedLanguages}) is zero.
126: *
127: * </td></tr><tr><td align="left" valign="top" width="1%"><font size="+1">
128: * <b>Evolution</b></font></td><td>
129: *
130: * No method should be removed from this class, but some functionality can
131: * be added in future.
132: *
133: * </td></tr></tbody></table>
134: *
135: * @author Jan Jancura
136: */
137: public final class Session implements ContextProvider {
138:
139: /** Name of property for current language. */
140: public static final String PROP_CURRENT_LANGUAGE = "currentLanguage";
141:
142: /** Name of property for the set of supported languages. */
143: public static final String PROP_SUPPORTED_LANGUAGES = "supportedLanguages";
144:
145: // variables ...............................................................
146:
147: private String name;
148: private String locationName;
149: private DebuggerEngine currentDebuggerEngine;
150: private String currentLanguage;
151: private String[] languages;
152: private DebuggerEngine[] engines;
153: // private Listener listener;
154: private PropertyChangeSupport pcs;
155: private Lookup lookup;
156: Lookup privateLookup;
157:
158: // initialization ..........................................................
159:
160: Session(String name, String locationName, String id,
161: Object[] services, Lookup diLookup) {
162: this .name = name;
163: this .locationName = locationName;
164: this .languages = new String[0];
165: this .engines = new DebuggerEngine[0];
166: pcs = new PropertyChangeSupport(this );
167:
168: // create lookup
169: Object[] s = new Object[services.length + 1];
170: System.arraycopy(services, 0, s, 0, services.length);
171: s[s.length - 1] = this ;
172: privateLookup = new Lookup.Compound(new Lookup.Instance(s),
173: new Lookup.MetaInf(id));
174: this .lookup = new Lookup.Compound(diLookup, privateLookup);
175: }
176:
177: // public interface ........................................................
178:
179: /**
180: * Returns display name of this session.
181: *
182: * @return display name of this session
183: */
184: public String getName() {
185: return name;
186: }
187:
188: /**
189: * Returns identifier of type of this session. This id is used for
190: * identification of engine during registration of services in
191: * Meta-inf/debugger.
192: *
193: * @return identifier of type of this engine
194: */
195: // public String getTypeID () {
196: // return id;
197: // }
198: /**
199: * Returns name of location this session is running on.
200: *
201: * @return name of location this session is running on
202: */
203: public String getLocationName() {
204: return locationName;
205: }
206:
207: /**
208: * Returns current debugger engine for this session.
209: *
210: * @return current debugger engine for this session
211: */
212: public DebuggerEngine getCurrentEngine() {
213: return currentDebuggerEngine;
214: }
215:
216: /**
217: * Returns current language for this session.
218: *
219: * @return current language for this session
220: */
221: public String getCurrentLanguage() {
222: return currentLanguage;
223: }
224:
225: /**
226: * Returns set of all languages supported by this session.
227: *
228: * @return set of all languages supported by this session
229: * @see org.netbeans.spi.debugger.DebuggerEngineProvider
230: */
231: public String[] getSupportedLanguages() {
232: return languages;
233: }
234:
235: /**
236: * Returns list of services of given type from given folder.
237: *
238: * @param service a type of service to look for
239: * @return list of services of given type
240: */
241: public <T> List<? extends T> lookup(String folder, Class<T> service) {
242: return lookup.lookup(folder, service);
243: }
244:
245: /**
246: * Returns one service of given type from given folder.
247: *
248: * @param service a type of service to look for
249: * @return ne service of given type
250: */
251: public <T> T lookupFirst(String folder, Class<T> service) {
252: return lookup.lookupFirst(folder, service);
253: }
254:
255: /**
256: * Kills all registerred engines / languages. This utility method calls
257: * <pre>doAction (DebuggerEngine.ACTION_KILL)</pre> method on all
258: * registerred DebuggerEngines.
259: */
260: public void kill() {
261: HashSet dead = new HashSet(Arrays.asList(engines));
262: Iterator i = dead.iterator();
263: while (i.hasNext())
264: ((DebuggerEngine) i.next()).getActionsManager().doAction(
265: ActionsManager.ACTION_KILL);
266: }
267:
268: /**
269: * Return DebuggerEngine registerred for given language or null.
270: *
271: * @return DebuggerEngine registerred for given language or null
272: */
273: public DebuggerEngine getEngineForLanguage(String language) {
274: int i, k = languages.length;
275: for (i = 0; i < k; i++)
276: if (languages[i].equals(language))
277: return engines[i];
278: return null;
279: }
280:
281: /**
282: * Sets current language for this session. Language should be refisterred
283: * for this session.
284: *
285: * @param language current language
286: * @see org.netbeans.spi.debugger.DebuggerEngineProvider
287: */
288: public void setCurrentLanguage(String language) {
289: int i, k = languages.length;
290: for (i = 0; i < k; i++) {
291: if (language.equals(languages[i])) {
292: Object oldL = currentLanguage;
293: currentLanguage = language;
294: currentDebuggerEngine = engines[i];
295: pcs.firePropertyChange(PROP_CURRENT_LANGUAGE, oldL,
296: currentLanguage);
297: }
298: }
299: }
300:
301: // support methods .........................................................
302:
303: Lookup getLookup() {
304: return lookup;
305: }
306:
307: void addLanguage(String language, DebuggerEngine engine) {
308: // is pair already added?
309: int i, k = languages.length;
310: for (i = 0; i < k; i++)
311: if (language.equals(languages[i])) {
312: engines[i] = engine;
313: return;
314: }
315:
316: // add pair
317: String[] newLanguages = new String[languages.length + 1];
318: DebuggerEngine[] newEngines = new DebuggerEngine[engines.length + 1];
319: System.arraycopy(languages, 0, newLanguages, 0,
320: languages.length);
321: System.arraycopy(engines, 0, newEngines, 0, engines.length);
322: newLanguages[languages.length] = language;
323: newEngines[engines.length] = engine;
324: Object oldL = languages;
325: languages = newLanguages;
326: engines = newEngines;
327: DebuggerManager.getDebuggerManager().addEngine(engine);
328: pcs.firePropertyChange(PROP_SUPPORTED_LANGUAGES, oldL,
329: languages);
330: // engine.addEngineListener (
331: // DebuggerEngineListener.PROP_ACTION_PERFORMED,
332: // listener
333: // );
334: if (currentLanguage == null) {
335: setCurrentLanguage(language);
336: }
337: }
338:
339: void removeEngine(DebuggerEngine engine) {
340: if (engines.length == 0)
341: return;
342: int i, k = engines.length;
343: ArrayList newLanguages = new ArrayList();
344: ArrayList newEngines = new ArrayList();
345: for (i = 0; i < k; i++)
346: if (!engine.equals(engines[i])) {
347: newLanguages.add(languages[i]);
348: newEngines.add(engines[i]);
349: }
350: String[] oldL = languages;
351: languages = (String[]) newLanguages
352: .toArray(new String[newLanguages.size()]);
353: engines = (DebuggerEngine[]) newEngines
354: .toArray(new DebuggerEngine[newEngines.size()]);
355: DebuggerManager.getDebuggerManager().removeEngine(engine);
356:
357: pcs.firePropertyChange(PROP_SUPPORTED_LANGUAGES, oldL,
358: languages);
359: }
360:
361: void removeLanguage(String language, DebuggerEngine engine) {
362: int i, k = languages.length;
363: for (i = 0; i < k; i++)
364: if (language.equals(languages[i])) {
365: if (engines[i] != engine)
366: throw new IllegalArgumentException();
367: break;
368: }
369: if (i >= k)
370: return;
371:
372: String[] newLanguages = new String[k - 1];
373: DebuggerEngine[] newEngines = new DebuggerEngine[k - 1];
374: if (i > 0) {
375: System.arraycopy(languages, 0, newLanguages, 0, i);
376: System.arraycopy(engines, 0, newEngines, 0, i);
377: }
378: System.arraycopy(languages, i + 1, newLanguages, i, k - i - 1);
379: System.arraycopy(engines, i + 1, newEngines, i, k - i - 1);
380: Object oldL = languages;
381: languages = newLanguages;
382: engines = newEngines;
383: pcs.firePropertyChange(PROP_SUPPORTED_LANGUAGES, oldL,
384: languages);
385:
386: k = engines.length;
387: for (i = 0; i < k; i++)
388: if (engines[i] == engine)
389: return;
390: DebuggerManager.getDebuggerManager().removeEngine(engine);
391: }
392:
393: /**
394: * Returns string representation of this session.
395: *
396: * @return string representation of this session
397: */
398: public String toString() {
399: return "" + getClass().getName() + " " + getLocationName()
400: + ":" + getName();
401: }
402:
403: // listener support ........................................................
404:
405: /**
406: * Adds a property change listener.
407: *
408: * @param l the listener to add
409: */
410: public void addPropertyChangeListener(PropertyChangeListener l) {
411: pcs.addPropertyChangeListener(l);
412: }
413:
414: /**
415: * Removes a property change listener.
416: *
417: * @param l the listener to remove
418: */
419: public void removePropertyChangeListener(PropertyChangeListener l) {
420: pcs.removePropertyChangeListener(l);
421: }
422:
423: /**
424: * Adds a property change listener.
425: *
426: * @param propertyName a name of property to listen on
427: * @param l the listener to add
428: */
429: public void addPropertyChangeListener(String propertyName,
430: PropertyChangeListener l) {
431: pcs.addPropertyChangeListener(propertyName, l);
432: }
433:
434: /**
435: * Removes a property change listener.
436: *
437: * @param propertyName a name of property to stop listening on
438: * @param l the listener to remove
439: */
440: public void removePropertyChangeListener(String propertyName,
441: PropertyChangeListener l) {
442: pcs.removePropertyChangeListener(propertyName, l);
443: }
444:
445: // innerclasses ............................................................
446:
447: // private class Listener extends DebuggerEngineAdapter {
448: //
449: // public void actionPerformed (
450: // DebuggerEngine engine,
451: // Object action,
452: // boolean success
453: // ) {
454: // if (action != DebuggerEngine.ACTION_KILL) return;
455: // removeEngine (engine);
456: // }
457: //
458: // public void actionStateChanged (
459: // DebuggerEngine engine,
460: // Object action,
461: // boolean enabled
462: // ) {
463: // }
464: //
465: // }
466: }
|