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-2007 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: /*
043: * File : DefaultSinkManager.java
044: * Version : 1.0
045: * Description : Manages the sinks (listeners) attached to the running
046: * Describe instance.
047: * Author : Trey Spiva
048: */
049: package org.netbeans.modules.uml.integration.ide;
050:
051: import org.netbeans.modules.uml.core.eventframework.EventDispatchNameKeeper;
052: import org.netbeans.modules.uml.core.eventframework.IEventDispatchController;
053: import org.netbeans.modules.uml.core.eventframework.IEventDispatcher;
054: import org.netbeans.modules.uml.core.metamodel.core.foundation.ElementLifeTimeEventDispatcher;
055: import org.netbeans.modules.uml.core.metamodel.core.foundation.IElementLifeTimeEventDispatcher;
056: import org.netbeans.modules.uml.core.roundtripframework.IRoundTripAttributeEventsSink;
057: import org.netbeans.modules.uml.core.roundtripframework.IRoundTripClassEventsSink;
058: import org.netbeans.modules.uml.core.roundtripframework.IRoundTripEnumEventsSink;
059: import org.netbeans.modules.uml.core.roundtripframework.IRoundTripEnumLiteralEventsSink;
060: import org.netbeans.modules.uml.core.roundtripframework.IRoundTripController;
061: import org.netbeans.modules.uml.core.roundtripframework.IRoundTripEventDispatcher;
062: import org.netbeans.modules.uml.core.roundtripframework.IRoundTripRelationEventsSink;
063: import org.netbeans.modules.uml.core.support.umlmessagingcore.IMessengerEventsSink;
064: import org.netbeans.modules.uml.core.support.umlmessagingcore.IUMLMessagingEventDispatcher;
065: import org.netbeans.modules.uml.core.support.umlsupport.Log;
066: import org.netbeans.modules.uml.core.workspacemanagement.IWSProjectEventsSink;
067: import org.netbeans.modules.uml.core.workspacemanagement.IWorkspaceEventDispatcher;
068: import org.netbeans.modules.uml.core.workspacemanagement.IWorkspaceEventsSink;
069: import org.netbeans.modules.uml.ui.controls.drawingarea.DrawingAreaEventDispatcherImpl;
070: import org.netbeans.modules.uml.ui.controls.drawingarea.IDrawingAreaEventDispatcher;
071: import org.netbeans.modules.uml.ui.controls.projecttree.IProjectTreeContextMenuEventsSink;
072: import org.netbeans.modules.uml.ui.controls.projecttree.IProjectTreeEventDispatcher;
073: import org.netbeans.modules.uml.ui.support.DispatchHelper;
074:
075: /**
076: * Manages the sinks (listeners) attached to the running Describe instance.
077: *
078: * Revision History
079: * No. Date Who What
080: * --- ---- --- ----
081: * 1 2002-05-22 Darshan Added support for source navigation.
082: *
083: * @author Trey Spiva
084: * @version 1.0
085: */
086: public class DefaultSinkManager extends SourceNavigable {
087: public static final String RT_LANGUAGE = "Java";
088: ElementLifeTimeEventDispatcher d;
089: private IRoundTripClassEventsSink mRTClassSink;
090: private RoundTripPackageEventsSink mRTPackageSink;
091: private IRoundTripRelationEventsSink mRTRelationSink;
092: DispatchHelper helper = null;
093:
094: public DefaultSinkManager() {
095: helper = new DispatchHelper();
096: }
097:
098: public DefaultSinkManager(IEventDispatchController controller) {
099: setEventDispatchController(controller);
100: }
101:
102: public void setEventDispatchController(
103: IEventDispatchController controller) {
104: mController = controller;
105: }
106:
107: // public void registerForAllWorkspaceEvents() {
108: // registerForWorkspaceEvents();
109: // registerForWSProjectEvents();
110: // }
111:
112: public void initializeAll() {
113: Log.out("initializeAll() - Registering for all events");
114: registerForRoundTripEvents();
115: // registerForAllWorkspaceEvents();
116: // registerForMessagesEvents();
117: // registerForDrawingAreaEvents();
118: // registerForAllTreeEvents();
119: // registerForSCMEvents();
120: // registerForPreferenceEvents();
121: // // Was commented out during the C++ days
122: // registerForElementLifeTimeEvents();
123: }
124:
125: // private void registerForPreferenceEvents()
126: // {
127: // if(helper == null)
128: // helper = new DispatchHelper();
129: //
130: // helper.registerForPreferenceManagerEvents(
131: // new DefaultPreferenceManagerEventSink());
132: // }
133:
134: /**
135: * Connects a sink to the Workspace dispatcher.
136: // */
137: // public void registerForWorkspaceEvents() {
138: // if (mController != null) {
139: // establishWorkspaceDispatcher();
140: // if (mWDispatcher != null) {
141: // IWorkspaceEventsSink sink = getWorkspaceEventsSink();
142: // if (sink == null)
143: // setWorkspaceEventsSink(sink = new WorkspaceEventsSink());
144: // mWDispatcher.registerForWorkspaceEvents(sink);
145: // Log.out(
146: // "In registerForWorkspaceEvents() : Registered for Workspace Events");
147: // GDProSupport
148: // .getGDProSupport()
149: // .getSinkManager()
150: // .registerForRoundTripEvents();
151: // Log.out(
152: // "In registerForWorkspaceEvents() : Registered for RoundTrip Events");
153: // }
154: // }
155: // }
156: /**
157: * Connects a sink to the Workspace Dispatcher
158: */
159: // public void registerForWSProjectEvents() {
160: // if (mController != null) {
161: // establishWorkspaceDispatcher();
162: // if (mWDispatcher != null) {
163: // IWSProjectEventsSink sink = getProjectEventsSink();
164: // if (sink == null)
165: // setProjectEventsSink(sink = new WSProjectEventsSink());
166: // mWDispatcher.registerForWSProjectEvents(sink);
167: // Log.out(
168: // "In registerForWorkspaceEvents() : Registered for WSProject Events");
169: // }
170: // }
171: // }
172: /**
173: * Connects sinks to all the ProjectTree event sinks
174: */
175: // public void registerForAllTreeEvents() {
176: // registerForTreeMenuEvents();
177: // registerForProjectTreeEvents();
178: // }
179: /**
180: * Connects a sink to the context menu events coming off the Project tree
181: */
182: // public void registerForTreeMenuEvents() {
183: // if (mController != null) {
184: // establishTreeDispatcher();
185: //
186: // IProjectTreeContextMenuEventsSink sink =
187: // getProjectTreeContextMenuEventsSink();
188: // if (sink == null)
189: // setProjectTreeContextMenuEventsSink(
190: // sink = new ProjectTreeContextMenuEventsSink());
191: //
192: // mTreeDispatcher.registerProjectTreeContextMenuEvents(sink);
193: // }
194: // }
195: /**
196: * Connects a sink to the tree events, such as selection and double clicks
197: */
198: // public void registerForProjectTreeEvents() {
199: // if (mController != null) {
200: // establishTreeDispatcher();
201: // DefaultTreeEventsSink tree = getProjectTreeEventsSink();
202: // if (tree == null)
203: // setProjectTreeEventsSink(tree = new DefaultTreeEventsSink());
204: // tree.setSourceNavigator(navigator);
205: // mTreeDispatcher.registerProjectTreeEvents(tree);
206: // }
207: // }
208: public void registerForProcessorEvents() {
209: IRoundTripController rt = UMLSupport.getUMLSupport()
210: .getProduct().getRoundTripController();
211:
212: if (rt != null) {
213: // Need to get the dispatch controller off the RoundTrip controller
214: // to listen for the OnInitialized()
215:
216: IRoundTripEventDispatcher dispatcher = rt
217: .getRoundTripDispatcher();
218:
219: if (dispatcher != null) {
220: Log
221: .out("Regsitering for init events on request processor");
222: dispatcher
223: .registerForRequestProcessorInitEvents(new RequestProcessorInitEventsSink());
224: } else {
225: Log.out("RT Dispatcher null");
226: }
227: } else {
228: Log.out("RT null");
229: }
230: }
231:
232: public void registerForRoundTripEvents() {
233: Log.out("registering for round trip events");
234: IRoundTripController rt = UMLSupport.getUMLSupport()
235: .getProduct().getRoundTripController();
236:
237: // Need to get the dispatch controller off the RoundTrip controller
238: // to listen for the OnInitialized()
239:
240: IRoundTripEventDispatcher dispatcher = rt
241: .getRoundTripDispatcher();
242:
243: if (mRTAttrSink != null)
244: dispatcher.revokeRoundTripAttributeSink(mRTAttrSink);
245: dispatcher.registerForRoundTripAttributeEvents(
246: mRTAttrSink = new RoundTripAttributeEventsSink(),
247: RT_LANGUAGE);
248:
249: if (mRTOperSink != null)
250: dispatcher.revokeRoundTripOperationSink(mRTOperSink);
251: dispatcher.registerForRoundTripOperationEvents(
252: mRTOperSink = new RoundTripOperationEventsSink(),
253: RT_LANGUAGE);
254: if (mRTClassSink != null) {
255: dispatcher.revokeRoundTripClassSink(mRTClassSink);
256: dispatcher
257: .revokeRoundTripEnumSink((IRoundTripEnumEventsSink) mRTClassSink);
258: }
259: dispatcher.registerForRoundTripClassEvents(
260: mRTClassSink = new RoundTripClassEventsSink(),
261: RT_LANGUAGE);
262: dispatcher.registerForRoundTripEnumEvents(
263: (IRoundTripEnumEventsSink) mRTClassSink, RT_LANGUAGE);
264:
265: if (mRTPackageSink != null)
266: dispatcher.revokeRoundTripPackageSink(mRTPackageSink);
267: dispatcher.registerForRoundTripPackageEvents(
268: mRTPackageSink = new RoundTripPackageEventsSink(),
269: RT_LANGUAGE);
270: if (mRTRelationSink != null)
271: dispatcher.revokeRoundTripRelationSink(mRTRelationSink);
272: dispatcher.registerForRoundTripRelationEvents(
273: mRTRelationSink = new RoundTripRelationEventsSink(),
274: RT_LANGUAGE);
275:
276: if (mRTEnumLiteralSink != null)
277: dispatcher
278: .revokeRoundTripEnumLiteralSink(mRTEnumLiteralSink);
279: dispatcher
280: .registerForRoundTripEnumLiteralEvents(
281: mRTEnumLiteralSink = new RoundTripEnumLiteralEventsSink(),
282: RT_LANGUAGE);
283:
284: if (eventFrameworkSink != null)
285: dispatcher.revokeEventFrameworkSink(eventFrameworkSink);
286: dispatcher
287: .registerForEventFrameworkEvents(eventFrameworkSink = new EventFrameworkSink());
288: eventFrameworkSink.setSourceNavigator(navigator);
289:
290: Log.out("registered for round trip events ..");
291: }
292:
293: /**
294: * Pulls the WorkspaceEventDispatcher off the internal EventDispatchController
295: */
296: // protected void establishWorkspaceDispatcher() {
297: // if (mController != null && (mWDispatcher == null)) {
298: // IEventDispatcher disp =
299: // mController.retrieveDispatcher("WorkspaceDispatcher");
300: //
301: // mWDispatcher = (IWorkspaceEventDispatcher) disp;
302: // }
303: // }
304: /**
305: * Pulls the ProjectTreeEventDispatcher off the internal EventDispatchController
306: */
307: // protected void establishTreeDispatcher() {
308: // if (mController != null && mTreeDispatcher == null) {
309: // if(helper == null)
310: // helper = new DispatchHelper();
311: // mTreeDispatcher = helper.getProjectTreeDispatcher();
312: // }
313: // }
314: /**
315: * Connects a sink to the Drawing Area
316: */
317: // public void registerForDrawingAreaEvents() {
318: // Log.out("Registering for drawing area events");
319: // if (mController != null) {
320: // establishDrawingAreaDispatcher();
321: // DefaultDrawingAreaSelectionEventsSink selSink =
322: // getDrawingSelectionEventsSink();
323: // if (selSink == null)
324: // setDrawingSelectionEventsSink(
325: // selSink = new DefaultDrawingAreaSelectionEventsSink());
326: // selSink.setSourceNavigator(navigator);
327: // mDrawingAreaDispatcher.registerDrawingAreaSelectionEvents(selSink);
328: //
329: // DefaultCompartmentEventsSink compSink =
330: // getDrawingCompartmentEventsSink();
331: // if (compSink == null)
332: // setDrawingCompartmentEventsSink(
333: // compSink = new DefaultCompartmentEventsSink());
334: // compSink.setSourceNavigator(navigator);
335: //
336: // mDrawingAreaDispatcher.registerDrawingAreaCompartmentEvents(
337: // compSink);
338: // DefaultDrawingAreaEventsSink eventSink = getDrawingAreaEventsSink();
339: //
340: // if (eventSink == null)
341: // setDrawingAreaEventsSink(
342: // eventSink = new DefaultDrawingAreaEventsSink());
343: //
344: // mDrawingAreaDispatcher.registerDrawingAreaEvents(eventSink);
345: // }
346: // else
347: // Log.out("mController is null!");
348: // }
349: /**
350: * Connects a sink to the Describe messages
351: */
352: // public void registerForMessagesEvents() {
353: // Log.out("Registering for messages events");
354: // if (mController != null) {
355: // establishMessagesDispatcher();
356: // DefaultMessagesEventsSink messSink = getMessagesEventsSink();
357: // if (messSink == null)
358: // setMessagesEventsSink(
359: // messSink = new DefaultMessagesEventsSink());
360: //
361: // Log.out("DefaultSinkManager.registerForMessagesEvents: " +
362: // "Registering preference watcher");
363: // Preferences.addPreferenceWatcher(
364: // Preferences.LOG_DESCRIBE_MESSAGES,
365: // new Preferences.PreferenceWatcher() {
366: // private IMessengerEventsSink mMessagingSink;
367: //
368: // public void preferenceChanged(String preference,
369: // String oldValue,
370: // String newValue) {
371: // if (Preferences.PSK_NO.equals(newValue)) {
372: // if (mMessagingSink != null) {
373: // Log.out(
374: // "DefaultSinkManager." +
375: // "registerForMessagesEvents: " +
376: // "Revoking messaging sink");
377: //
378: // mMessagesDispatcher.revokeMessengerSink(
379: // mMessagingSink);
380: // mMessagingSink = null;
381: // }
382: // }
383: // else {
384: // if (mMessagingSink == null) {
385: // Log.out(
386: // "DefaultSinkManager." +
387: // "registerForMessagesEvents: " +
388: // "Registering messaging sink");
389: // mMessagesDispatcher
390: // .registerMessengerEvents(
391: // mMessagingSink =
392: // getMessagesEventsSink());
393: // }
394: // }
395: // }
396: // },
397: // true
398: // );
399: // }
400: // else
401: // Log.out("mController is null!");
402: // }
403: /**
404: * Connects a sink to the SCM events
405: */
406: // public void registerForSCMEvents() {
407: // Log.out("Registering for SCM events");
408: // if (mController != null) {
409: // establishSCMDispatcher();
410: // DefaultSCMEventsSink scmSink = getSCMEventsSink();
411: // if (scmSink == null)
412: // setSCMEventsSink(scmSink = new DefaultSCMEventsSink());
413: // // TODO
414: //// mSCMDispatcher.registerForSCMEvents(scmSink);
415: // }
416: // else
417: // Log.out("mController is null!");
418: // }
419:
420: /**
421: * Pulls the ElementLifeTimeEventsDispatcher off the internal EventDispatchController
422: */
423: // protected void establishElementLifeTimeEventsDispatcher() {
424: // if (mController != null && mLifeTimeDispatcher == null) {
425: // if(helper == null)
426: // helper = new DispatchHelper();
427: // mLifeTimeDispatcher = helper.getLifeTimeDispatcher();
428: // Log.out("establishElementLifeTimeDispatcher() : " + mLifeTimeDispatcher.getClass().getName());
429: // }
430: // }
431: // public void registerForElementLifeTimeEvents() {
432: // Log.out("Registering for ElementLifeTime events");
433: // if (mController != null) {
434: // establishElementLifeTimeEventsDispatcher();
435: // DefaultElementLifeTimeEventsSink lifeTimeSink = getElementLifeTimeEventsSink();
436: // if (lifeTimeSink == null)
437: // setElementLifeTimeEventsSink(lifeTimeSink = new DefaultElementLifeTimeEventsSink());
438: // Log.out("registerForElementLifeTimeEvents() - " + lifeTimeSink);
439: // mLifeTimeDispatcher.registerForLifeTimeEvents(lifeTimeSink);
440: // }
441: // else
442: // Log.out("mController is null!");
443: // }
444:
445: /**
446: * Pulls the DrawingAreaEventDispatcher off the internal
447: * EventDispatchController
448: */
449: // protected void establishDrawingAreaDispatcher() {
450: // if (mController != null && mDrawingAreaDispatcher == null) {
451: // if(helper == null)
452: // helper = new DispatchHelper();
453: // mDrawingAreaDispatcher = helper.getDrawingAreaDispatcher();
454: // }
455: // }
456: // protected void establishMessagesDispatcher() {
457: // if (mController != null && (mMessagesDispatcher == null)) {
458: // IEventDispatcher disp =
459: // mController.retrieveDispatcher("UMLMessagingDispatcher");
460: // mMessagesDispatcher =
461: // (IUMLMessagingEventDispatcher) disp;
462: // }
463: // }
464: // protected void establishSCMDispatcher() {
465: // if (mController != null /* && TODO (mSCMDispatcher == null) */) {
466: // // IEventDispatcher disp = mController.retrieveDispatcher("SCM");
467: // IEventDispatcher disp =
468: // GDProSupport.getGDProSupport().getProduct().getEventDispatcher(
469: // "SCM");
470: // // TODO
471: //// mSCMDispatcher = (ISCMEventDispatcher) disp;
472: // }
473: // }
474: public void purge() {
475: mWDispatcher = null;
476: mTreeDispatcher = null;
477: // TODO
478: // mSCMDispatcher = null;
479: mController = null;
480: mDrawingAreaDispatcher = null;
481: }
482:
483: // public IWorkspaceEventsSink getWorkspaceEventsSink() {
484: // return workspaceEventsSink;
485: // }
486:
487: // public void setWorkspaceEventsSink(IWorkspaceEventsSink workspaceEventsSink) {
488: // this.workspaceEventsSink = workspaceEventsSink;
489: // }
490:
491: // public IWSProjectEventsSink getProjectEventsSink() {
492: // return projectEventsSink;
493: // }
494:
495: public void setProjectEventsSink(
496: IWSProjectEventsSink projectEventsSink) {
497: this .projectEventsSink = projectEventsSink;
498: }
499:
500: // public DefaultTreeEventsSink getProjectTreeEventsSink() {
501: // return projectTreeEventsSink;
502: // }
503:
504: // public void setProjectTreeEventsSink(DefaultTreeEventsSink projectTreeEventsSink) {
505: // this.projectTreeEventsSink = projectTreeEventsSink;
506: // }
507:
508: // public IProjectTreeContextMenuEventsSink getProjectTreeContextMenuEventsSink() {
509: // return projectTreeContextMenuEventsSink;
510: // }
511:
512: // public void setProjectTreeContextMenuEventsSink(IProjectTreeContextMenuEventsSink sink) {
513: // this.projectTreeContextMenuEventsSink = sink;
514: // }
515: //
516: // public DefaultDrawingAreaSelectionEventsSink getDrawingSelectionEventsSink() {
517: // return drawingSelectionEventsSink;
518: // }
519: //
520: // public DefaultElementLifeTimeEventsSink getElementLifeTimeEventsSink() {
521: // return lifeTimeEventsSink;
522: // }
523: //
524: //
525: // public void setDrawingSelectionEventsSink(DefaultDrawingAreaSelectionEventsSink sink) {
526: // this.drawingSelectionEventsSink = sink;
527: // }
528: //
529: // public DefaultMessagesEventsSink getMessagesEventsSink() {
530: // return messagesEventsSink;
531: // }
532: //
533: // public DefaultSCMEventsSink getSCMEventsSink() {
534: // return scmEventsSink;
535: // }
536: //
537: // public void setMessagesEventsSink(DefaultMessagesEventsSink sink) {
538: // this.messagesEventsSink = sink;
539: // }
540: //
541: // public void setSCMEventsSink(DefaultSCMEventsSink sink) {
542: // this.scmEventsSink = sink;
543: // }
544: //
545: // public void setElementLifeTimeEventsSink(DefaultElementLifeTimeEventsSink sink) {
546: // this.lifeTimeEventsSink = sink;
547: // }
548: //
549: //
550: // public DefaultCompartmentEventsSink getDrawingCompartmentEventsSink() {
551: // return drawingCompartmentEventsSink;
552: // }
553: //
554: // public void setDrawingCompartmentEventsSink(DefaultCompartmentEventsSink sink) {
555: // this.drawingCompartmentEventsSink = sink;
556: // }
557: //
558: // public DefaultDrawingAreaEventsSink getDrawingAreaEventsSink() {
559: // return drawingAreaEventsSink;
560: // }
561: //
562: // public void setDrawingAreaEventsSink(DefaultDrawingAreaEventsSink sink) {
563: // this.drawingAreaEventsSink = sink;
564: // }
565: //
566: public EventFrameworkSink getEventFrameworkSink() {
567: return eventFrameworkSink;
568: }
569:
570: protected IWorkspaceEventDispatcher mWDispatcher;
571: protected IProjectTreeEventDispatcher mTreeDispatcher;
572: protected IEventDispatchController mController;
573: protected IDrawingAreaEventDispatcher mDrawingAreaDispatcher;
574: protected IUMLMessagingEventDispatcher mMessagesDispatcher;
575: // protected ISCMEventDispatcher mSCMDispatcher;
576: private IElementLifeTimeEventDispatcher mLifeTimeDispatcher;
577:
578: protected IWorkspaceEventsSink workspaceEventsSink;
579: protected IWSProjectEventsSink projectEventsSink;
580: // protected DefaultTreeEventsSink projectTreeEventsSink;
581: protected IProjectTreeContextMenuEventsSink projectTreeContextMenuEventsSink;
582: // protected DefaultDrawingAreaSelectionEventsSink drawingSelectionEventsSink;
583: // protected DefaultMessagesEventsSink messagesEventsSink;
584: // protected DefaultSCMEventsSink scmEventsSink;
585: // protected DefaultElementLifeTimeEventsSink lifeTimeEventsSink;
586: // protected DefaultCompartmentEventsSink drawingCompartmentEventsSink;
587: // protected DefaultDrawingAreaEventsSink drawingAreaEventsSink;
588: protected EventFrameworkSink eventFrameworkSink;
589:
590: protected IRoundTripAttributeEventsSink mRTAttrSink;
591: protected RoundTripOperationEventsSink mRTOperSink;
592: protected IRoundTripEnumLiteralEventsSink mRTEnumLiteralSink;
593:
594: }
|