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.jsp.tag;
016:
017: import java.io.Writer;
018: import java.util.HashMap;
019: import java.util.HashSet;
020: import java.util.Iterator;
021: import java.util.Map;
022: import java.util.Set;
023: import javax.servlet.jsp.JspException;
024: import javax.servlet.jsp.PageContext;
025: import javax.servlet.jsp.jstl.fmt.LocalizationContext;
026: import javax.servlet.jsp.tagext.Tag;
027: import javax.servlet.jsp.tagext.TryCatchFinally;
028: import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
029: import org.araneaframework.Environment;
030: import org.araneaframework.OutputData;
031: import org.araneaframework.core.ApplicationWidget;
032: import org.araneaframework.core.AraneaRuntimeException;
033: import org.araneaframework.http.util.ServletUtil;
034: import org.araneaframework.jsp.exception.AraneaJspException;
035: import org.araneaframework.jsp.tag.context.WidgetContextTag;
036: import org.araneaframework.jsp.tag.uilib.WidgetTag;
037: import org.araneaframework.jsp.util.JspUtil;
038: import org.araneaframework.jsp.util.JspWidgetUtil;
039: import org.araneaframework.uilib.ConfigurationContext;
040:
041: /**
042: * UI contained base tag.
043: *
044: * @author Oleg Mürk
045: * @author Alar Kvell (alar@araneaframework.org)
046: */
047: public class BaseTag implements Tag, TryCatchFinally,
048: ContainedTagInterface {
049: /* ***********************************************************************************
050: * VARIABLES
051: * ***********************************************************************************/
052: protected Tag parent;
053: protected PageContext pageContext;
054:
055: /**
056: * A list of registered tags.
057: */
058: private Set registeredTags;
059:
060: /**
061: * Map: scope -> (Map: key -> backup attribute value)
062: */
063: private Map attributeBackup;
064:
065: private Set globalContextEntries;
066: private Map hiddenContextEntries;
067: public static final String GLOBAL_CONTEXT_ENTRIES_KEY = "org.araneaframework.jsp.tag.BaseTag.GLOBAL_CONTEXT_ENTRIES";
068:
069: /* ***********************************************************************************
070: * Tag interface methods && ContainedTagInterface methods
071: * ***********************************************************************************/
072:
073: /**
074: * Initialization.
075: */
076: public void setPageContext(PageContext pageContext) {
077: this .pageContext = pageContext;
078:
079: // Internal initialization
080: registeredTags = new HashSet();
081: attributeBackup = new HashMap();
082: }
083:
084: /**
085: * Start tag.
086: */
087: public final int doStartTag() throws JspException {
088: try {
089: return doStartTag(pageContext.getOut());
090: } catch (RuntimeException e) {
091: throw e;
092: } catch (JspException e) {
093: throw e;
094: } catch (Exception e) {
095: throw new JspException(e);
096: }
097: }
098:
099: /**
100: * End tag
101: */
102: public final int doEndTag() throws JspException {
103: try {
104: return doEndTag(pageContext.getOut());
105: } catch (RuntimeException e) {
106: throw e;
107: } catch (JspException e) {
108: throw e;
109: } catch (Exception e) {
110: throw new JspException(e);
111: }
112: }
113:
114: public void setParent(Tag tag) {
115: this .parent = tag;
116: }
117:
118: public Tag getParent() {
119: return parent;
120: }
121:
122: /**
123: * Deinitialization.
124: */
125: public void release() {
126: }
127:
128: /* ***********************************************************************************
129: * TryCatchFinally interface methods
130: * ***********************************************************************************/
131:
132: public void doCatch(Throwable t) throws Throwable {
133: throw t;
134: }
135:
136: /** Override it to free additional resources, always call superclass method too. */
137: public void doFinally() {
138: releaseTags();
139: restoreAllContextEntries();
140: resetGlobalContextEntries();
141: }
142:
143: /* ***********************************************************************************
144: * Tags writing out start and end tags.
145: * ***********************************************************************************/
146: /**
147: * Internal callback: before tag.
148: * @throws Exception
149: */
150: protected int doStartTag(Writer out) throws Exception {
151: return EVAL_BODY_INCLUDE;
152: }
153:
154: /**
155: * Internal callback: after tag.
156: * @throws Exception
157: */
158: protected int doEndTag(Writer out) throws Exception {
159: return EVAL_PAGE;
160: }
161:
162: /* ***********************************************************************************
163: * Context entry managing methods
164: * ***********************************************************************************/
165: /**
166: * Gets the value of <code>key</code> from <code>PageContext.REQUEST_SCOPE</code>.
167: */
168: protected Object getContextEntry(String key) {
169: return JspUtil.getContextEntry(pageContext, key);
170: }
171:
172: /**
173: * Read attribute value in PageContext.REQUEST_SCOPE and ensure that it is defined.
174: * @throws JspException when entry corresponding to key is not found
175: */
176: protected Object requireContextEntry(String key)
177: throws JspException {
178: return JspUtil.requireContextEntry(pageContext, key);
179: }
180:
181: /**
182: * Set attribute value in given scope, but allow restoring it to the state before
183: * executing this action.
184: */
185: protected void addContextEntry(String key, Object value) {
186: Map attributeBackupMap = getBackupContextEntryMap(PageContext.REQUEST_SCOPE);
187:
188: // Backup value
189: Object currentAttribute = pageContext.getAttribute(key,
190: PageContext.REQUEST_SCOPE);
191: if (!attributeBackupMap.containsKey(key))
192: attributeBackupMap.put(key, currentAttribute);
193:
194: // Set new value
195: setGlobalContextEntry(key, value);
196: if (value != null)
197: pageContext.setAttribute(key, value,
198: PageContext.REQUEST_SCOPE);
199: else
200: pageContext.removeAttribute(key, PageContext.REQUEST_SCOPE);
201: }
202:
203: /* ***********************************************************************************
204: * Attribute evaluation methods
205: * ***********************************************************************************/
206:
207: /**
208: * Evaluates attribute value and checks that it is not null.
209: */
210: protected Object evaluateNotNull(String attributeName,
211: String attributeValue, Class classObject)
212: throws JspException {
213: Object value = evaluate(attributeName, attributeValue,
214: classObject);
215: if (value == null)
216: throw new AraneaJspException("Attribute '" + attributeName
217: + "' should not evaluate to null");
218: return value;
219: }
220:
221: /**
222: * Evaluates attribute value.
223: */
224: protected Object evaluate(String attributeName,
225: String attributeValue, Class classObject)
226: throws JspException {
227: return ExpressionEvaluatorManager.evaluate(attributeName,
228: attributeValue, classObject, this , pageContext);
229: }
230:
231: /* ***********************************************************************************
232: * Subtag managing methods
233: * ***********************************************************************************/
234: /**
235: * Registers a subtag.
236: */
237: protected void registerSubtag(ContainedTagInterface subtag) {
238: subtag.setPageContext(pageContext);
239: registeredTags.add(subtag);
240: }
241:
242: /**
243: * Unregisters a subtag.
244: */
245: protected void unregisterSubtag(ContainedTagInterface subtag) {
246: subtag.doFinally();
247:
248: subtag.release();
249: registeredTags.remove(subtag);
250: }
251:
252: /**
253: * Executes registered subtag.
254: */
255: protected int executeSubtag(ContainedTagInterface subtag)
256: throws JspException {
257: int result = subtag.doStartTag();
258: if (result == Tag.EVAL_BODY_INCLUDE)
259: return subtag.doEndTag();
260: else
261: return result;
262: }
263:
264: /**
265: * Executes start of registered subtag.
266: */
267: protected int executeStartSubtag(ContainedTagInterface subtag)
268: throws JspException {
269: return subtag.doStartTag();
270: }
271:
272: /**
273: * Executes end of registered subtag.
274: */
275: protected int executeEndSubtag(ContainedTagInterface subtag)
276: throws JspException {
277: return subtag.doEndTag();
278: }
279:
280: protected int registerAndExecuteStartTag(
281: ContainedTagInterface subtag) throws JspException {
282: registerSubtag(subtag);
283: return executeStartSubtag(subtag);
284: }
285:
286: protected int executeEndTagAndUnregister(
287: ContainedTagInterface subtag) throws JspException {
288: int result = executeEndSubtag(subtag);
289: unregisterSubtag(subtag);
290: return result;
291: }
292:
293: //
294: // Service methods
295: //
296:
297: protected ConfigurationContext getConfiguration() {
298: return (ConfigurationContext) getEnvironment().getEntry(
299: ConfigurationContext.class);
300: }
301:
302: /**
303: * Returns the current <code>LocalizationContext</code>.
304: * @return current <code>LocalizationContext</code>.
305: */
306: protected LocalizationContext getLocalizationContext() {
307: return JspUtil.getLocalizationContext(pageContext);
308: }
309:
310: /**
311: * Returns the current response object.
312: * @return the current response object.
313: */
314: protected OutputData getOutputData() {
315: return ServletUtil.getOutputData(pageContext.getRequest());
316: }
317:
318: /**
319: * @since 1.1
320: */
321: protected Environment getEnvironment() {
322: return ServletUtil.getEnvironment(pageContext.getRequest());
323: }
324:
325: /**
326: * @since 1.1
327: */
328: protected ApplicationWidget getContextWidget() {
329: return JspWidgetUtil.getContextWidget(pageContext);
330: }
331:
332: /**
333: * @since 1.1
334: */
335: protected String getContextWidgetFullId() {
336: return JspWidgetUtil.getContextWidgetFullId(pageContext);
337: }
338:
339: /* ***********************************************************************************
340: * PRIVATE internal method for releasing the subtags
341: * ***********************************************************************************/
342: private void releaseTags() {
343: for (Iterator i = registeredTags.iterator(); i.hasNext();) {
344: ContainedTagInterface subtag = (ContainedTagInterface) i
345: .next();
346:
347: subtag.doFinally();
348: subtag.release();
349:
350: i.remove();
351: }
352: }
353:
354: /* ***********************************************************************************
355: * PRIVATE internal methods for context entry managing.
356: * ***********************************************************************************/
357: /**
358: * Get backup attribute map for given scope.
359: */
360: private Map getBackupContextEntryMap(int scope) {
361: if (attributeBackup == null)
362: attributeBackup = new HashMap();
363:
364: Map map = (Map) attributeBackup.get(new Integer(scope));
365: if (map == null) {
366: map = new HashMap();
367: attributeBackup.put(new Integer(scope), map);
368: }
369: return map;
370: }
371:
372: /**
373: * Restores all attributes to the values before executing this action.
374: */
375: private void restoreAllContextEntries() {
376: if (attributeBackup == null)
377: return;
378:
379: for (Iterator i = attributeBackup.entrySet().iterator(); i
380: .hasNext();) {
381: Map.Entry attributeBackupEntry = (Map.Entry) i.next();
382: int scope = ((Integer) attributeBackupEntry.getKey())
383: .intValue();
384: Map attributeBackupMap = (Map) attributeBackupEntry
385: .getValue();
386:
387: for (Iterator j = attributeBackupMap.entrySet().iterator(); j
388: .hasNext();) {
389: Map.Entry entry2 = (Map.Entry) j.next();
390: Object oldAttribute = entry2.getValue();
391: setGlobalContextEntry((String) entry2.getKey(),
392: oldAttribute);
393: if (oldAttribute != null)
394: pageContext.setAttribute((String) entry2.getKey(),
395: oldAttribute, scope);
396: else
397: pageContext.removeAttribute((String) entry2
398: .getKey(), scope);
399: }
400: }
401:
402: // Release data
403: attributeBackup = null;
404: }
405:
406: /* ***********************************************************************************
407: * Hiding and restoring contextentries when a child widget is rendered
408: * ***********************************************************************************/
409:
410: private void setGlobalContextEntry(String key, Object value) {
411: if (hiddenContextEntries != null) {
412: hiddenContextEntries = null;
413: throw new AraneaRuntimeException(
414: "ContextEntries were not restored properly");
415: }
416: if (value == null) {
417: if (globalContextEntries != null)
418: globalContextEntries.remove(key);
419: } else {
420: if (globalContextEntries == null) {
421: globalContextEntries = (Set) getContextEntry(GLOBAL_CONTEXT_ENTRIES_KEY);
422: if (globalContextEntries == null) {
423: globalContextEntries = new HashSet();
424: addContextEntry(GLOBAL_CONTEXT_ENTRIES_KEY,
425: globalContextEntries);
426: // Hide contextentries that are set in ServletUtil.include
427: globalContextEntries.add(ServletUtil.UIWIDGET_KEY);
428: globalContextEntries
429: .add(WidgetContextTag.CONTEXT_WIDGET_KEY);
430: globalContextEntries
431: .add(Environment.ENVIRONMENT_KEY);
432: globalContextEntries.add(WidgetTag.WIDGET_KEY);
433: globalContextEntries.add(WidgetTag.WIDGET_ID_KEY);
434: globalContextEntries
435: .add(WidgetTag.WIDGET_VIEW_MODEL_KEY);
436: globalContextEntries
437: .add(WidgetTag.WIDGET_VIEW_DATA_KEY);
438: //XXX also hide ServletUtil.LOCALIZATION_CONTEXT_KEY ?
439: }
440: }
441: globalContextEntries.add(key);
442: }
443: }
444:
445: /**
446: * @since 1.1
447: */
448: protected void hideGlobalContextEntries(PageContext pageContext) {
449: if (globalContextEntries == null)
450: globalContextEntries = (Set) getContextEntry(GLOBAL_CONTEXT_ENTRIES_KEY);
451: if (globalContextEntries == null
452: || globalContextEntries.size() == 0)
453: return;
454: if (hiddenContextEntries != null) {
455: hiddenContextEntries = null;
456: throw new AraneaRuntimeException(
457: "ContextEntries were not restored properly");
458: }
459: hiddenContextEntries = new HashMap();
460: for (Iterator i = globalContextEntries.iterator(); i.hasNext();) {
461: String key = (String) i.next();
462: Object value = pageContext.getAttribute(key,
463: PageContext.REQUEST_SCOPE);
464: if (value != null) {
465: hiddenContextEntries.put(key, value);
466: pageContext.removeAttribute(key,
467: PageContext.REQUEST_SCOPE);
468: }
469: }
470: }
471:
472: /**
473: * @since 1.1
474: */
475: protected void restoreGlobalContextEntries(PageContext pageContext) {
476: if (hiddenContextEntries == null)
477: return;
478: for (Iterator i = hiddenContextEntries.entrySet().iterator(); i
479: .hasNext();) {
480: Map.Entry entry = (Map.Entry) i.next();
481: pageContext.setAttribute((String) entry.getKey(), entry
482: .getValue(), PageContext.REQUEST_SCOPE);
483: }
484: hiddenContextEntries = null;
485: }
486:
487: private void resetGlobalContextEntries() {
488: if (globalContextEntries != null) {
489: globalContextEntries = null;
490: }
491: if (hiddenContextEntries != null) {
492: hiddenContextEntries = null;
493: throw new AraneaRuntimeException(
494: "ContextEntries were not restored properly");
495: }
496: }
497:
498: }
|