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.util;
016:
017: import java.io.IOException;
018: import java.io.Writer;
019: import java.util.ArrayList;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024: import java.util.MissingResourceException;
025: import java.util.StringTokenizer;
026: import javax.servlet.ServletException;
027: import javax.servlet.jsp.JspException;
028: import javax.servlet.jsp.PageContext;
029: import javax.servlet.jsp.jstl.fmt.LocalizationContext;
030: import org.apache.commons.lang.StringEscapeUtils;
031: import org.araneaframework.http.util.ServletUtil;
032: import org.araneaframework.jsp.UiEvent;
033: import org.araneaframework.jsp.exception.AraneaJspException;
034: import org.araneaframework.jsp.tag.PresentationTag;
035: import org.araneaframework.jsp.tag.basic.AttributedTagInterface;
036: import org.araneaframework.jsp.tag.basic.ElementHtmlTag;
037: import org.araneaframework.jsp.tag.layout.support.CellClassProvider;
038: import org.araneaframework.jsp.tag.layout.support.RowClassProvider;
039: import org.araneaframework.jsp.tag.uilib.form.FormElementTag;
040: import org.araneaframework.jsp.tag.uilib.form.FormTag;
041: import org.araneaframework.jsp.tag.uilib.list.ListRowsTag;
042: import org.araneaframework.jsp.tag.uilib.list.ListTag;
043: import org.araneaframework.jsp.tag.uilib.list.formlist.FormListTag;
044:
045: /**
046: * UI common utilities.
047: *
048: * @author Oleg Mürk
049: */
050: public class JspUtil {
051: private static final Map attributeErrorMap = new HashMap();
052: static {
053: attributeErrorMap.put(
054: AttributedTagInterface.ATTRIBUTED_TAG_KEY, null);
055: attributeErrorMap.put(PresentationTag.ATTRIBUTED_TAG_KEY, null);
056:
057: attributeErrorMap.put(FormListTag.FORM_LIST_ID_KEY,
058: "<ui:formList> tag expected, but not found!");
059: attributeErrorMap.put(FormListTag.FORM_LIST_VIEW_MODEL_KEY,
060: "<ui:formList> tag expected, but not found!");
061:
062: attributeErrorMap
063: .put(
064: ElementHtmlTag.KEY,
065: "<ui:element> tag expected, but not found! Probably this is an attempt to use <ui:elementContent> or <ui:attribute> outside <ui:element> tag.");
066:
067: attributeErrorMap
068: .put(
069: FormElementTag.ID_KEY,
070: "<ui:formElement> tag expected, but not found! Make sure that form element and control tags either have an 'id' or are used inside <ui:formElement> tag.");
071:
072: attributeErrorMap
073: .put(
074: FormTag.FORM_VIEW_MODEL_KEY,
075: "<ui:form> tag expected, but not found! Make sure form element and control tags are used inside <ui:form> tag.");
076: attributeErrorMap
077: .put(
078: FormTag.FORM_FULL_ID_KEY,
079: "<ui:form> tag expected, but not found! Make sure form element and control tags are used inside <ui:form> tag.");
080: attributeErrorMap
081: .put(
082: FormTag.FORM_KEY,
083: "<ui:form> tag expected, but not found! Make sure form element and control tags are used inside <ui:form> tag.");
084:
085: attributeErrorMap
086: .put(
087: RowClassProvider.KEY,
088: "<ui:layout> tag expected, but not found! Make sure that row tags are used inside <ui:layout> tag.");
089: attributeErrorMap
090: .put(
091: CellClassProvider.KEY,
092: "<ui:layout> or <ui:row> expected, but not found! Make sure that row and cell tags are inside inside <ui:layout> tag.");
093:
094: attributeErrorMap
095: .put(
096: ListTag.LIST_VIEW_MODEL_KEY,
097: "<ui:list> tag expected, but not found! Make sure list tags is used inside <ui:list> tag.");
098: attributeErrorMap
099: .put(
100: ListTag.LIST_ID_KEY,
101: "<ui:list> tag expected, but not found! Make sure list tags is used inside <ui:list> tag.");
102: attributeErrorMap
103: .put(ListRowsTag.ROW_REQUEST_ID_KEY,
104: "<ui:listRows> or another list rows tag expected, but not found!");
105: }
106:
107: /**
108: * Includes JSP page at given path.
109: */
110: public static void include(PageContext pageContext, String path)
111: throws ServletException, IOException {
112: // starting with '/' is absolute path (may add prefix), otherwise path is relative (unchanged).
113: pageContext.include(path.startsWith("/") ? "/content" + path
114: : path);
115: }
116:
117: /**
118: * Get resource string for given id.
119: * Throws MissingResourceException when given resource not found.
120: */
121: public static String getResourceString(PageContext pageContext,
122: String id) {
123: return getLocalizationContext(pageContext).getResourceBundle()
124: .getString(id);
125: }
126:
127: /**
128: * Get resource string for given id.
129: * Return null when given string not found
130: */
131: public static String getResourceStringOrNull(
132: PageContext pageContext, String id) {
133: try {
134: return getLocalizationContext(pageContext)
135: .getResourceBundle().getString(id);
136: } catch (MissingResourceException e) {
137: return null;
138: }
139: }
140:
141: public static LocalizationContext getLocalizationContext(
142: PageContext pageContext) {
143: return (LocalizationContext) pageContext.getRequest()
144: .getAttribute(ServletUtil.LOCALIZATION_CONTEXT_KEY);
145: }
146:
147: /**
148: * Writes opening of start tag
149: */
150: public static void writeOpenStartTag(Writer out, String tag)
151: throws IOException {
152: out.write("<");
153: out.write(tag);
154: }
155:
156: /**
157: * Writes closing of start tag
158: */
159: public static void writeCloseStartTag(Writer out)
160: throws IOException {
161: out.write(">");
162: out.write("\n");
163: }
164:
165: /**
166: * Writes closing of start tag. Space sensitive.
167: */
168: public static void writeCloseStartTag_SS(Writer out)
169: throws IOException {
170: out.write(">");
171: }
172:
173: /**
174: * Writes closing of start tag that is also and end tag.
175: */
176: public static void writeCloseStartEndTag(Writer out)
177: throws IOException {
178: out.write("/>");
179: out.write("\n");
180: }
181:
182: /**
183: * Writes closing of start tag that is also and end tag. Space sensitive.
184: */
185: public static void writeCloseStartEndTag_SS(Writer out)
186: throws IOException {
187: out.write("/>");
188: }
189:
190: /**
191: * Writes start tag.
192: */
193: public static void writeStartTag(Writer out, String tag)
194: throws IOException {
195: out.write("<");
196: out.write(tag);
197: out.write(">");
198: out.write("\n");
199: }
200:
201: /**
202: * Writes end tag. Space sensitive.
203: */
204: public static void writeStartTag_SS(Writer out, String tag)
205: throws IOException {
206: out.write("<");
207: out.write(tag);
208: out.write(">");
209: }
210:
211: /**
212: * Writes start tag that is also and end tag.
213: */
214: public static void writeStartEndTag(Writer out, String tag)
215: throws IOException {
216: out.write("<");
217: out.write(tag);
218: out.write("/>");
219: out.write("\n");
220: }
221:
222: /**
223: * Writes start tag that is also and end tag. Space sensitive.
224: */
225: public static void writeStartEndTag_SS(Writer out, String tag)
226: throws IOException {
227: out.write("<");
228: out.write(tag);
229: out.write("/>");
230: }
231:
232: /**
233: * Writes end tag.
234: */
235: public static void writeEndTag(Writer out, String tag)
236: throws IOException {
237: out.write("</");
238: out.write(tag);
239: out.write(">");
240: out.write("\n");
241: }
242:
243: /**
244: * Writes end tag. Space sensitive.
245: */
246: public static void writeEndTag_SS(Writer out, String tag)
247: throws IOException {
248: out.write("</");
249: out.write(tag);
250: out.write(">");
251: }
252:
253: /**
254: * Writes out attributes contained in the Map <attributeName, attributeValue>.
255: * If map is <code>null</code>, writes nothing.
256: */
257: public static void writeAttributes(Writer out, Map attributes)
258: throws IOException {
259: if (attributes == null)
260: return;
261:
262: for (Iterator i = attributes.entrySet().iterator(); i.hasNext();) {
263: Map.Entry entry = (Map.Entry) i.next();
264: String attributeName = (String) entry.getKey();
265: JspUtil
266: .writeAttribute(out, attributeName, entry
267: .getValue());
268: }
269: }
270:
271: /**
272: * Writers attribute of form ' ATTR_NAME="value"'.
273: * If value is <code>null</code>, writes nothing.
274: * If escape is set to true HTML escaping takes place on
275: * the value (<, >, ", & get replaced with
276: * the entities).
277: */
278: public static void writeAttribute(Writer out, String name,
279: Object value, boolean escape) throws IOException {
280: if (value == null)
281: return;
282:
283: out.write(" ");
284: out.write(name);
285: out.write("=\"");
286: if (escape)
287: writeEscapedAttribute(out, value.toString());
288: else
289: out.write(value.toString());
290: out.write("\"");
291: }
292:
293: public static void writeAttribute(Writer out, String name,
294: Object value) throws IOException {
295: writeAttribute(out, name, value, true);
296: }
297:
298: /**
299: * Writes opening of attribute.
300: */
301: public static void writeOpenAttribute(Writer out, String name)
302: throws IOException {
303: out.write(" ");
304: out.write(name);
305: out.write("=\"");
306: }
307:
308: /**
309: * Writes closing of attribute.
310: */
311: public static void writeCloseAttribute(Writer out)
312: throws IOException {
313: out.write("\"");
314: }
315:
316: /**
317: * Writes script string.
318: * Equivalent to <code>writeScriptString(out, value, true)</code>
319: */
320: public static void writeScriptString(Writer out, String value)
321: throws IOException {
322: out.write("'");
323: writeEscapedScriptString(out, value, true);
324: out.write("'");
325: }
326:
327: /**
328: * Writes given String properly formatted for javascript in single quotes.
329: * @param escapeEntities set it to true if you want to escape XML entities like &amp;, <lt; etc.
330: * @see #writeEscapedScriptString
331: */
332: public static void writeScriptString(Writer out, String value,
333: boolean escapeEntities) throws IOException {
334: out.write("'");
335: writeEscapedScriptString(out, value, escapeEntities);
336: out.write("'");
337: }
338:
339: /**
340: * Writes script string or expression evaluating to a string.
341: *
342: * @param out
343: * @param value string value
344: * @param expression expression
345: *
346: * @throws AraneaJspException if both value and expression are specified
347: */
348: public static void writeScriptString_rt(Writer out, String value,
349: String expression) throws IOException, AraneaJspException {
350: if (value != null && expression != null)
351: throw new AraneaJspException(
352: "String value and run-time expression should not be specified at the same time");
353:
354: if (expression != null) {
355: writeEscaped(out, expression);
356: } else {
357: out.write("'");
358: writeEscapedScriptString(out, value, true);
359: out.write("'");
360: }
361: }
362:
363: /**
364: * Writes out escaped string. <code>null</code> values are omitted.
365: */
366: public static void writeEscaped(Writer out, String value)
367: throws IOException {
368: if (value == null)
369: return;
370: out.write(StringEscapeUtils.escapeHtml(value));
371: }
372:
373: /**
374: * Writes out escaped attribute string. <code>null</code> values are omitted.
375: */
376: public static void writeEscapedAttribute(Writer out, String value)
377: throws IOException {
378: if (value == null)
379: return;
380:
381: for (int i = 0; i < value.length(); i++) {
382: char c = value.charAt(i);
383: switch (c) {
384: case '<':
385: out.write("<");
386: break;
387: case '>':
388: out.write(">");
389: break;
390: case '&':
391: out.write("&");
392: break;
393: case '"':
394: out.write(""");
395: break;
396: case '\n':
397: out.write("&xA;");
398: break;
399: default:
400: out.write(c);
401: }
402: }
403: }
404:
405: /**
406: * Writes out escaped script string (can be used also in attributes). <code>null</code> values are omitted.
407: * @param escapeEntities - when this is true, standard XML entities are used to escape
408: * greater-than, less-than, ampersand and double-quotes. Otherwise, these symbols are
409: * left as they are. Set this attribute to true, if you write javascript inside an attribute,
410: * and set it to false if you write javascript inside a <script> tag.
411: */
412: public static void writeEscapedScriptString(Writer out,
413: String value, boolean escapeEntities) throws IOException {
414: if (value == null)
415: return;
416:
417: for (int i = 0; i < value.length(); i++) {
418: char c = value.charAt(i);
419: switch (c) {
420: case '<':
421: out.write(escapeEntities ? "<" : "<");
422: break;
423: case '>':
424: out.write(escapeEntities ? ">" : ">");
425: break;
426: case '&':
427: out.write(escapeEntities ? "&" : "&");
428: break;
429: case '"':
430: out.write(escapeEntities ? """ : "\"");
431: break;
432: case '\'':
433: out.write("\\'");
434: break;
435: case '\n':
436: out.write("\\n");
437: break;
438: case '\\':
439: out.write("\\\\");
440: break;
441: default:
442: out.write(c);
443: }
444: }
445: }
446:
447: public static void writeEventAttributes(Writer out, UiEvent event)
448: throws IOException {
449: out.write(" ");
450: out.write(event.getEventAttributes().toString());
451: out.write(" ");
452: }
453:
454: /**
455: * Writes out hidden html input element with give name and value.
456: */
457: public static void writeHiddenInputElement(Writer out, String name,
458: String value) throws IOException {
459: JspUtil.writeOpenStartTag(out, "input");
460: JspUtil.writeAttribute(out, "name", name);
461: JspUtil.writeAttribute(out, "type", "hidden");
462: JspUtil.writeAttribute(out, "value", value);
463: JspUtil.writeCloseStartEndTag(out);
464: }
465:
466: /**
467: * Parses multi-valued attribute, where attributes are separated by commas.
468: * Empty attribute values are allowed, they are specified by including whitespace
469: * between commas: "first, ,third".
470: * @return List<String> containing attribute values.
471: */
472: public static List parseMultiValuedAttribute(String attribute) {
473: List result = new ArrayList();
474:
475: if (attribute != null && !"".equals(attribute.trim())) {
476: StringTokenizer tokens = new StringTokenizer(attribute, ",");
477: while (tokens.hasMoreTokens())
478: result.add(tokens.nextToken().trim());
479: }
480:
481: return result;
482: }
483:
484: // -------------- Operations with PageContext ------------------- //
485: /**
486: * Read attribute value from request scope.
487: */
488: public static Object getContextEntry(PageContext pageContext,
489: String key) {
490: return pageContext.getAttribute(key, PageContext.REQUEST_SCOPE);
491: }
492:
493: /**
494: * Read attribute value from request scope and ensure that it is defined.
495: * @throws AraneaJspException if key is not present in given <code>PageContext</code>
496: */
497: public static Object requireContextEntry(PageContext pageContext,
498: String key) throws JspException {
499: Object value = pageContext.getAttribute(key,
500: PageContext.REQUEST_SCOPE);
501: if (value == null) {
502: StringBuffer message = new StringBuffer();
503: String errMsg = (String) attributeErrorMap.get(key);
504: if (errMsg != null)
505: message.append(errMsg + " (");
506: message.append("Missing attribute '" + key + "' in ");
507: message.append("'PageContext.REQUEST_SCOPE'");
508: message.append(" scope");
509: if (errMsg != null)
510: message.append(")");
511: throw new AraneaJspException(message.toString());
512: } else
513: return value;
514: }
515: }
|