001: /**
002: * Copyright 2006 Webmedia Group Ltd.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: **/package org.araneaframework.core;
016:
017: import java.util.ArrayList;
018: import java.util.Collections;
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.List;
022: import java.util.Map;
023: import org.apache.commons.collections.map.LinkedMap;
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026: import org.araneaframework.Component;
027: import org.araneaframework.Composite;
028: import org.araneaframework.Environment;
029: import org.araneaframework.InputData;
030: import org.araneaframework.Message;
031: import org.araneaframework.OutputData;
032: import org.araneaframework.Path;
033: import org.araneaframework.Scope;
034: import org.araneaframework.Service;
035: import org.araneaframework.Viewable;
036: import org.araneaframework.Widget;
037: import org.araneaframework.core.util.ExceptionUtil;
038:
039: /**
040: * A full featured Widget with support for composite, eventlisteners, viewmodel.
041: *
042: */
043: public abstract class BaseApplicationWidget extends BaseWidget
044: implements ApplicationWidget {
045:
046: //*******************************************************************
047: // CONSTANTS
048: //*******************************************************************
049:
050: private static final Log log = LogFactory
051: .getLog(BaseApplicationWidget.class);
052:
053: //*******************************************************************
054: // FIELDS
055: //*******************************************************************
056: private Map eventListeners;
057: private EventListener globalListener;
058:
059: private Map actionListeners;
060:
061: private Map viewData;
062: private Map viewDataOnce;
063:
064: //*******************************************************************
065: // CONSTRUCTORS
066: //*******************************************************************
067:
068: //*******************************************************************
069: // PROTECTED CLASSES
070: //*******************************************************************
071: protected class ViewableImpl implements Viewable.Interface {
072: public Object getViewModel() {
073: try {
074: return BaseApplicationWidget.this .getViewModel();
075: } catch (Exception e) {
076: throw ExceptionUtil.uncheckException(e);
077: }
078: }
079: }
080:
081: protected class CompositeImpl implements Composite.Interface {
082: /**
083: * Returns a map of all the child components under this Composite.
084: * @return a map of child components
085: */
086: public Map getChildren() {
087: return BaseApplicationWidget.this .getChildren();
088: }
089:
090: /**
091: * @see org.araneaframework.Composite.Interface#attach(java.lang.Object, org.araneaframework.Component)
092: */
093: public void attach(Object key, Component comp) {
094: _getChildren().put(key, comp);
095: }
096:
097: /**
098: * @see org.araneaframework.Composite.Interface#detach(java.lang.Object)
099: */
100: public Component detach(Object key) {
101: return (Component) _getChildren().remove(key);
102: }
103: }
104:
105: //*******************************************************************
106: // PRIVATE CLASSES
107: //*******************************************************************
108:
109: //*******************************************************************
110: // PUBLIC CLASSES
111: //*******************************************************************
112:
113: public class ViewModel implements ApplicationWidget.WidgetViewModel {
114: private Map viewData;
115:
116: public ViewModel() {
117: viewData = BaseApplicationWidget.this .viewData == null ? new HashMap()
118: : new HashMap(BaseApplicationWidget.this .viewData);
119: if (viewDataOnce != null)
120: viewData.putAll(viewDataOnce);
121: }
122:
123: /** @since 1.1 */
124: public Scope getScope() {
125: return BaseApplicationWidget.this .getScope();
126: }
127:
128: public Map getChildren() {
129: return BaseApplicationWidget.this .getChildren();
130: }
131:
132: public Map getData() {
133: return viewData;
134: }
135: }
136:
137: //*******************************************************************
138: // PRIVATE METHODS
139: //*******************************************************************
140:
141: private Map getEventListeners() {
142: if (eventListeners == null)
143: eventListeners = new LinkedMap(1);
144:
145: return eventListeners;
146: }
147:
148: private Map getActionListeners() {
149: if (actionListeners == null)
150: actionListeners = new LinkedMap(1);
151:
152: return actionListeners;
153: }
154:
155: private Map getViewData() {
156: if (viewData == null)
157: viewData = new LinkedMap(1);
158:
159: return viewData;
160: }
161:
162: private Map getViewDataOnce() {
163: if (viewDataOnce == null)
164: viewDataOnce = new LinkedMap(1);
165:
166: return viewDataOnce;
167: }
168:
169: //*******************************************************************
170: // PROTECTED METHODS
171: //*******************************************************************
172:
173: /**
174: * Returns the widget's Environment by default. Usually overridden.
175: */
176: protected Environment getChildWidgetEnvironment() throws Exception {
177: return getEnvironment();
178: }
179:
180: protected void propagate(Message message) throws Exception {
181: _propagate(message);
182: }
183:
184: protected void update(InputData input) throws Exception {
185: if (viewDataOnce != null)
186: viewDataOnce.clear();
187:
188: handleUpdate(input);
189:
190: Iterator ite = (new HashMap(getChildren())).keySet().iterator();
191: while (ite.hasNext()) {
192: Object key = ite.next();
193: Component c = (Component) getChildren().get(key);
194: if (c != null && c instanceof Widget) {
195: ((Widget) c)._getWidget().update(input);
196: }
197: }
198: }
199:
200: /**
201: * If path hasNextStep() routes to the correct child,
202: * otherwise calls the appropriate listener.
203: */
204: protected void event(Path path, InputData input) throws Exception {
205: if (path != null && path.hasNext()) {
206: Object next = path.next();
207:
208: Assert.notNull(this , next,
209: "Cannot deliver event to child under null key!");
210:
211: Widget pWidget = (Widget) getChildren().get(next);
212:
213: if (pWidget == null) {
214: if (log.isWarnEnabled()) {
215: log.warn("Widget '" + getScope()
216: + "' could not deliver event as child '"
217: + next + "' was not found or not a Widget!"
218: + Assert.this ToString(this ));
219: }
220: return;
221: }
222:
223: pWidget._getWidget().event(path, input);
224: } else {
225: handleEvent(input);
226: }
227: }
228:
229: /**
230: * Callback called when <code>update(InputData)</code> is invoked.
231: */
232: protected void handleUpdate(InputData input) throws Exception {
233: }
234:
235: /**
236: * Calls the respective listeners.
237: */
238: protected void handleEvent(InputData input) throws Exception {
239: String eventId = getEventId(input);
240:
241: if (eventId == null) {
242: if (log.isWarnEnabled()) {
243: log
244: .warn("Widget '"
245: + getScope()
246: + "' cannot deliver event for a null action id!"
247: + Assert.this ToString(this ));
248: }
249: return;
250: }
251:
252: List listener = eventListeners == null ? null
253: : (List) eventListeners.get(eventId);
254:
255: if (log.isTraceEnabled()) {
256: log.trace("Delivering event '" + eventId + "' to widget '"
257: + getScope() + "', type: '" + getClass().getName()
258: + "'");
259: }
260:
261: try {
262: if (listener != null && listener.size() > 0) {
263: Iterator ite = (new ArrayList(listener)).iterator();
264: while (ite.hasNext()) {
265: ((EventListener) ite.next()).processEvent(eventId,
266: input);
267: }
268:
269: return;
270: }
271:
272: if (globalListener != null) {
273: globalListener.processEvent(eventId, input);
274: return;
275: }
276: } catch (Exception e) {
277: throw new EventException(this , String.valueOf(getScope()),
278: eventId, e);
279: }
280:
281: if (log.isWarnEnabled()) {
282: log
283: .warn("Widget '"
284: + getScope()
285: + "' cannot deliver event as no event listeners were registered for the event id '"
286: + eventId + "'!"
287: + Assert.this ToString(this ));
288: }
289:
290: }
291:
292: /**
293: * If {@link Path#hasNext()} routes to the action to child, otherwise calls the
294: * appropriate {@link ActionListener}.
295: */
296: protected void action(Path path, InputData input, OutputData output)
297: throws Exception {
298: if (path != null && path.hasNext()) {
299: Object next = path.next();
300:
301: Assert.notNull(this , next,
302: "Cannot deliver action to child under null key!");
303:
304: Service service = (Service) getChildren().get(next);
305: if (service == null) {
306: log.warn("Service '" + getScope()
307: + "' could not deliver action as child '"
308: + next + "' was not found!"
309: + Assert.this ToString(this ));
310: return;
311: }
312:
313: service._getService().action(path, input, output);
314: } else {
315: handleAction(input, output);
316: }
317: }
318:
319: /**
320: * Calls the approriate listener
321: */
322: protected void handleAction(InputData input, OutputData output)
323: throws Exception {
324: Object actionId = getActionId(input);
325:
326: if (actionId == null) {
327: if (log.isWarnEnabled()) {
328: log
329: .warn("Service '"
330: + getScope()
331: + "' cannot deliver action for a null action id!"
332: + Assert.this ToString(this ));
333: }
334: return;
335: }
336:
337: List listener = actionListeners == null ? null
338: : (List) actionListeners.get(actionId);
339:
340: if (log.isTraceEnabled()) {
341: log.trace("Delivering action '" + actionId
342: + "' to service '" + getClass() + "'");
343: }
344:
345: if (log.isTraceEnabled()) {
346: log.trace("Delivering action '" + actionId
347: + "' to service '" + getScope() + "', type: '"
348: + getClass().getName() + "'");
349: }
350:
351: if (listener != null && listener.size() > 0) {
352: Iterator ite = (new ArrayList(listener)).iterator();
353: while (ite.hasNext()) {
354: ((ActionListener) ite.next()).processAction(actionId,
355: input, output);
356: }
357:
358: return;
359: }
360:
361: if (log.isWarnEnabled()) {
362: log
363: .warn("Service '"
364: + getScope()
365: + "' cannot deliver action as no action listeners were registered for action id '"
366: + actionId + "'!"
367: + Assert.this ToString(this ));
368: }
369: }
370:
371: /**
372: * Renders the component to output, meant for overriding.
373: */
374: protected void render(OutputData output) throws Exception {
375: }
376:
377: /**
378: * Returns the id of the event in InputData. By default returns EVENT_HANDLER_ID_KEY from
379: * the input's global data.
380: */
381: protected String getEventId(InputData input) {
382: return (String) input.getGlobalData().get(
383: ApplicationWidget.EVENT_HANDLER_ID_KEY);
384: }
385:
386: /**
387: * Returns the id of the action based on the input. Uses the ACTION_HANDLER_ID_KEY key
388: * to extract it from InputData's global data.
389: */
390: protected Object getActionId(InputData input) {
391: return input.getGlobalData().get(
392: ApplicationService.ACTION_HANDLER_ID_KEY);
393: }
394:
395: //*******************************************************************
396: // PUBLIC METHODS
397: //*******************************************************************
398: /**
399: * Returns the Viewable.Interface internal implementation.
400: * @return the Viewable.Interface implementation
401: */
402: public Viewable.Interface _getViewable() {
403: return new ViewableImpl();
404: }
405:
406: /**
407: * Returns the Composite.Interface internal implementation.
408: * @return the Composite.Interface implementation
409: */
410: public Composite.Interface _getComposite() {
411: return new CompositeImpl();
412: }
413:
414: /**
415: * Returns all the childcomponents of this component.
416: * @return a map of the childcomponents under this component
417: */
418: public Map getChildren() {
419: return Collections
420: .unmodifiableMap(new LinkedMap(_getChildren()));
421: }
422:
423: /**
424: * Returns the widget with the specified key.
425: * @param key of the child being returned
426: * @return the Widget under the provided key
427: */
428: public Widget getWidget(Object key) {
429: return (Widget) getChildren().get(key);
430: }
431:
432: /**
433: * Adds a widget as a child widget with the key. The child is initialized with the
434: * environment provided.
435: *
436: * @param key of the the child Widget
437: * @param child Widget being added
438: * @param env the Environment the child will be initialized with
439: */
440: public void addWidget(Object key, Widget child, Environment env) {
441: _addComponent(key, child, env);
442: }
443:
444: /** Adds a widget as a child widget with the key. The child is initialized with the
445: * Environment of this Widget
446: * @param key of the the child Widget
447: * @param child Widget being added
448: */
449: public void addWidget(Object key, Widget child) {
450: try {
451: addWidget(key, child, this .getChildWidgetEnvironment());
452: } catch (Exception e) {
453: throw ExceptionUtil.uncheckException(e);
454: }
455: }
456:
457: /**
458: * Removes component from the children and calls destroy on it.
459: * @param key of the child being removed
460: */
461: public void removeWidget(Object key) {
462: _removeComponent(key);
463: }
464:
465: /**
466: * Enables the widget with the specified key. Only a disabled widgets can be enabled.
467: */
468: public void enableWidget(Object key) {
469: _enableComponent(key);
470: }
471:
472: /**
473: * Disables the widget with the specified key. Only a enabled widgets can be disabled.
474: */
475: public void disableWidget(Object key) {
476: _disableComponent(key);
477: }
478:
479: public Environment getEnvironment() {
480: return super .getEnvironment();
481: }
482:
483: public final Environment getChildEnvironment() {
484: try {
485: return getChildWidgetEnvironment();
486: } catch (Exception e) {
487: throw ExceptionUtil.uncheckException(e);
488: }
489: }
490:
491: /**
492: * Adds a global eventlistener to this Widget. A global eventlistener gets
493: * all the events.
494: *
495: * @param eventListener a EventListener added as the global eventlistener.
496: */
497: public void setGlobalEventListener(EventListener eventListener) {
498: Assert.notNullParam(this , eventListener, "eventListener");
499: this .globalListener = eventListener;
500: }
501:
502: /**
503: * Clears the global eventlistener of this Widget.
504: */
505: public void clearGlobalEventListener() {
506: this .globalListener = null;
507: }
508:
509: /**
510: * Adds an EventListener to this Widget with an eventId. Multiple listeners
511: * can be added under one eventId.
512: *
513: * @param eventId the eventId of the listener
514: * @param listener the EventListener being added
515: * @see #removeEventListener
516: */
517: public void addEventListener(Object eventId, EventListener listener) {
518: Assert.notNullParam(this , eventId, "eventId");
519: Assert.notNullParam(this , listener, "listener");
520:
521: List list = (List) getEventListeners().get(eventId);
522:
523: if (list == null) {
524: list = new ArrayList(1);
525: }
526: list.add(listener);
527:
528: getEventListeners().put(eventId, list);
529: }
530:
531: /**
532: * Removes the listener from the Widget's eventlisteners.
533: * @param listener the EventListener to remove
534: * @see #addEventListener
535: */
536: public void removeEventListener(EventListener listener) {
537: Assert.notNullParam(this , listener, "listener");
538:
539: Iterator ite = (new HashMap(getEventListeners())).values()
540: .iterator();
541: while (ite.hasNext()) {
542: ((List) ite.next()).remove(listener);
543: }
544: }
545:
546: /**
547: * Clears all the EventListeners from this Widget with the specified eventId.
548: * @param eventId the id of the EventListeners.
549: */
550: public void clearEventlisteners(Object eventId) {
551: Assert.notNullParam(this , eventId, "eventId");
552:
553: getEventListeners().remove(eventId);
554: }
555:
556: /**
557: * Adds custom data to the widget view model (${widget.custom['key']}). This data will be
558: * available until explicitly removed with {@link #removeViewData(String)}.
559: */
560: public void putViewData(String key, Object customDataItem) {
561: Assert.notNullParam(this , key, "key");
562:
563: getViewData().put(key, customDataItem);
564: }
565:
566: /**
567: * Removes the custom data under key.
568: */
569: public void removeViewData(String key) {
570: Assert.notNullParam(this , key, "key");
571:
572: getViewData().remove(key);
573: }
574:
575: /**
576: * Adds custom data to the widget view model (${widget.custom['key']}). This data will be available during this
577: * request only.
578: */
579: public void putViewDataOnce(String key, Object customDataItem) {
580: Assert.notNullParam(this , key, "key");
581:
582: getViewDataOnce().put(key, customDataItem);
583: }
584:
585: /**
586: * Returns the view model. Usually overridden.
587: */
588: public Object getViewModel() throws Exception {
589: return new ViewModel();
590: }
591:
592: /**
593: * Adds the ActionListener listener with the specified action id.
594: */
595: public void addActionListener(Object actionId,
596: ActionListener listener) {
597: Assert.notNullParam(this , actionId, "actionId");
598: Assert.notNullParam(this , listener, "listener");
599:
600: List list = (List) getActionListeners().get(actionId);
601:
602: if (list == null) {
603: list = new ArrayList(1);
604: }
605: list.add(listener);
606:
607: getActionListeners().put(actionId, list);
608: }
609:
610: /**
611: * Removes the ActionListener listener from this component.
612: */
613: public void removeActionListener(ActionListener listener) {
614: Assert.notNullParam(this , listener, "listener");
615:
616: Iterator ite = (new HashMap(getActionListeners())).values()
617: .iterator();
618: while (ite.hasNext()) {
619: ((List) ite.next()).remove(listener);
620: }
621: }
622:
623: /**
624: * Clears all the ActionListeners with the specified actionId.
625: * @param actionId the actionId
626: */
627: public void clearActionListeners(Object actionId) {
628: Assert.notNullParam(this , actionId, "actionId");
629:
630: getActionListeners().remove(actionId);
631: }
632: }
|