View Javadoc

1   /*
2    * $Header: /cvsroot/jdbforms/dbforms/src/org/dbforms/taglib/AbstractDbBaseHandlerTag.java,v 1.7 2006/01/13 21:22:56 hkollmann Exp $
3    * $Revision: 1.7 $
4    * $Date: 2006/01/13 21:22:56 $
5    *
6    * DbForms - a Rapid Application Development Framework
7    * Copyright (C) 2001 Joachim Peer <joepeer@excite.com>
8    *
9    * This library is free software; you can redistribute it and/or
10   * modify it under the terms of the GNU Lesser General Public
11   * License as published by the Free Software Foundation; either
12   * version 2.1 of the License, or (at your option) any later version.
13   *
14   * This library is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this library; if not, write to the Free Software
21   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22   */
23  
24  package org.dbforms.taglib;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  
29  import org.dbforms.config.Constants;
30  import org.dbforms.config.Field;
31  import org.dbforms.config.FieldTypes;
32  import org.dbforms.config.ResultSetVector;
33  
34  import org.dbforms.event.AbstractWebEvent;
35  import org.dbforms.event.eventtype.EventType;
36  import org.dbforms.interfaces.IEscaper;
37  
38  import org.dbforms.util.MessageResources;
39  import org.dbforms.util.MessageResourcesInternal;
40  import org.dbforms.util.ParseUtil;
41  import org.dbforms.util.ReflectionUtil;
42  import org.dbforms.util.Util;
43  
44  import java.text.Format;
45  
46  import java.util.Locale;
47  import java.util.Vector;
48  
49  import javax.servlet.http.HttpServletRequest;
50  import javax.servlet.jsp.JspException;
51  
52  /***
53   * <p>
54   * Base class for db-tags that render form data-elements capable of including
55   * JavaScript event handlers and/or CSS Style attributes.
56   * </p>
57   * 
58   * <p>
59   * Furthermore, this base class provides base functionality for DataBase driven
60   * form widgets: it initializes the associated DbFormsConfig and provides
61   * various field-properties & methods (i.e getFormFieldName and
62   * getFormFieldValue)
63   * </p>
64   * 
65   * <p>
66   * the html/css releated properties and methods where originally done by Don
67   * Clasen for Apache Groups's Jakarta-Struts project.
68   * </p>
69   * 
70   * <p>
71   * Added support for Custom Formatter class, see SetCustomFormatter.java Author
72   * Neal Katz .
73   * </p>
74   * 
75   * <p>
76   * Added support for overrideFormFieldName attribute. useful when working with
77   * customcontrollers. Author Neal Katz .
78   * </p>
79   * orginally done by Don Clasen
80   * 
81   * @author Joe Peer (modified and extended this class for use in
82   *         DbForms-Project)
83   */
84  public abstract class AbstractDbBaseHandlerTag extends AbstractScriptHandlerTag {
85  	private static Log logCat = LogFactory.getLog(AbstractDbBaseHandlerTag.class
86  			.getName());
87  
88  	private DbFormTag parentForm;
89  
90  	private IEscaper escaper = null;
91  
92  	private Field field;
93  
94  	// n.k. Support for CustomFormatter attribute - neal katz
95  	private String customFormatter = null;
96  
97  	private String defaultValue;
98  
99  	private String escaperClass = null;
100 
101 	private String fieldName;
102 
103 	private String maxlength = null;
104 
105 	private String nullFieldValue;
106 
107 
108 	private String pattern;
109 
110 	private String readOnly = "false";
111 
112 	/*** Named Style class associated with component for read-only mode. */
113 	private String readOnlyStyleClass = null;
114 
115 	/***
116 	 * DOCUMENT ME!
117 	 * 
118 	 * @param string
119 	 */
120 	public void setCustomFormatter(String string) {
121 		customFormatter = string;
122 	}
123 
124 	/***
125 	 * DOCUMENT ME!
126 	 * 
127 	 * @param value
128 	 *            DOCUMENT ME!
129 	 */
130 	public void setDefaultValue(String value) {
131 		this.defaultValue = value;
132 	}
133 
134 	/***
135 	 * "value" is only used if parent tag is in "insert-mode" (footer, etc.)
136 	 * otherwise this tag takes the current value from the database result!
137 	 * 
138 	 * @return DOCUMENT ME!
139 	 */
140 	public String getDefaultValue() {
141 		return defaultValue;
142 	}
143 
144 	/***
145 	 * DOCUMENT ME!
146 	 * 
147 	 * @return DOCUMENT ME!
148 	 */
149 	public IEscaper getEscaper() {
150 		if (escaper == null) {
151 			String s = getEscaperClass();
152 
153 			if (!Util.isNull(s)) {
154 				try {
155 					escaper = (IEscaper) ReflectionUtil.newInstance(s);
156 				} catch (Exception e) {
157 					logCat
158 							.error("cannot create the new escaper [" + s + "]",
159 									e);
160 				}
161 			}
162 
163 			if ((escaper == null) && (getField() != null)) {
164 				escaper = getField().getEscaper();
165 			}
166 
167 			if ((escaper == null)) {
168 				escaper = getConfig().getEscaper();
169 			}
170 		}
171 
172 		return escaper;
173 	}
174 
175 	/***
176 	 * DOCUMENT ME!
177 	 * 
178 	 * @param string
179 	 */
180 	public void setEscaperClass(String string) {
181 		escaperClass = string;
182 	}
183 
184 	/***
185 	 * DOCUMENT ME!
186 	 * 
187 	 * @return
188 	 */
189 	public String getEscaperClass() {
190 		return escaperClass;
191 	}
192 
193 	/***
194 	 * DOCUMENT ME!
195 	 * 
196 	 * @return
197 	 */
198 	public Field getField() {
199 		return field;
200 	}
201 
202 	/***
203 	 * DOCUMENT ME!
204 	 * 
205 	 * @param fieldName
206 	 *            DOCUMENT ME!
207 	 */
208 	public void setFieldName(String fieldName) {
209 		this.fieldName = fieldName;
210 
211 		if (getParentForm().getTable() != null) {
212 			setField(getParentForm().getTable().getFieldByName(fieldName));
213 		} else {
214 			setField(null);
215 		}
216 
217 		if (getParentForm().isSubForm() && (this.field != null)) {
218 			// tell parent that _this_ class will generate the html tag, not
219 			// DbBodyTag!
220 			getParentForm().strikeOut(this.field);
221 		}
222 	}
223 
224 	/***
225 	 * formatting a value
226 	 * 
227 	 * @return DOCUMENT ME!
228 	 */
229 	protected Format getFormatter() {
230 		Format res = null;
231 		if (getField() != null) {
232 			res = getField().getFormat(pattern, getLocale());
233 		}
234 		return res;
235 	}
236 
237 	/***
238 	 * Sets the maxlength
239 	 * 
240 	 * @param maxlength
241 	 *            The maxlength to set
242 	 */
243 	public void setMaxlength(String maxlength) {
244 		this.maxlength = maxlength;
245 	}
246 
247 	/***
248 	 * Gets the maxlength
249 	 * 
250 	 * @return Returns a String
251 	 */
252 	public String getMaxlength() {
253 		return maxlength;
254 	}
255 
256 	/***
257 	 * DOCUMENT ME!
258 	 * 
259 	 * @return
260 	 */
261 	public String getName() {
262 		return (getField() != null) ? getField().getName() : fieldName;
263 	}
264 
265 	/***
266 	 * Sets the nullFieldValue attribute of the DbLabelTag object
267 	 * 
268 	 * @param nullFieldValue
269 	 *            The new nullFieldValue value
270 	 */
271 	public void setNullFieldValue(String nullFieldValue) {
272 		this.nullFieldValue = nullFieldValue;
273 	}
274 
275 	/***
276 	 * DOCUMENT ME!
277 	 * 
278 	 * @param parent
279 	 *            DOCUMENT ME!
280 	 */
281 	public void setParent(final javax.servlet.jsp.tagext.Tag parent) {
282 		super.setParent(parent);
283 
284 		// between this form and its parent lies a DbHeader/Body/Footer-Tag and
285 		// maybe other tags (styling, logic, etc.)
286 		parentForm = (DbFormTag) findAncestorWithClass(this, DbFormTag.class);
287 	}
288 
289 	/***
290 	 * DOCUMENT ME!
291 	 * 
292 	 * @param string
293 	 */
294 	public void setPattern(String string) {
295 		pattern = string;
296 	}
297 
298 	/***
299 	 * DOCUMENT ME!
300 	 * 
301 	 * @return
302 	 */
303 	public String getPattern() {
304 		Format f = getFormatter();
305 
306 		if (f == null) {
307 			return null;
308 		}
309 
310 		return Util.getPattern(f);
311 	}
312 
313 	/***
314 	 * Sets the read-only attribute.
315 	 * 
316 	 * @param readOnly
317 	 *            DOCUMENT ME!
318 	 */
319 	public void setReadOnly(String readOnly) {
320 		this.readOnly = readOnly;
321 	}
322 
323 	/***
324 	 * Sets the style class attribute for read-only mode.
325 	 * 
326 	 * @param readOnlyStyleClass
327 	 *            DOCUMENT ME!
328 	 */
329 	public void setReadOnlyStyleClass(String readOnlyStyleClass) {
330 		this.readOnlyStyleClass = readOnlyStyleClass;
331 	}
332 
333 	/***
334 	 * Returns the style class attribute for read-only mode.
335 	 * 
336 	 * @return DOCUMENT ME!
337 	 */
338 	public String getReadOnlyStyleClass() {
339 		return readOnlyStyleClass;
340 	}
341 
342 	/***
343 	 * DOCUMENT ME!
344 	 * 
345 	 * @return DOCUMENT ME!
346 	 */
347 	public String getStyleClass() {
348 		boolean readonly = hasReadOnlySet() || getParentForm().hasReadOnlySet();
349 
350 		if (readonly && !Util.isNull(getReadOnlyStyleClass())) {
351 			return getReadOnlyStyleClass();
352 		} else {
353 			return super.getStyleClass();
354 		}
355 	}
356 
357 	/***
358 	 * DOCUMENT ME!
359 	 * 
360 	 * @param s
361 	 * 
362 	 * @return
363 	 */
364 	protected String customFormat(String s) {
365 		return SetCustomFormatterTag.sprintf(customFormatter, pageContext, s);
366 	}
367 
368 	/***
369 	 * DOCUMENT ME!
370 	 */
371 	public void doFinally() {
372 		field = null;
373 		defaultValue = null;
374 		pattern = null;
375 		nullFieldValue = null;
376 		maxlength = null;
377 		readOnlyStyleClass = null;
378 		readOnly = "false";
379 		escaperClass = null;
380 		escaper = null;
381 		customFormatter = null;
382 		super.doFinally();
383 	}
384 
385 	/***
386 	 * Returns the read-only attribute.
387 	 * 
388 	 * @return DOCUMENT ME!
389 	 */
390 	public boolean hasReadOnlySet() {
391 		return Util.getTrue(readOnly);
392 	}
393 
394 	/***
395 	 * DOCUMENT ME!
396 	 * 
397 	 * @return
398 	 */
399 	protected String getCustomFormatter() {
400 		return customFormatter;
401 	}
402 
403 	/***
404 	 * DOCUMENT ME!
405 	 * 
406 	 * @param field
407 	 *            DOCUMENT ME!
408 	 */
409 	protected void setField(Field field) {
410 		this.field = field;
411 	}
412 
413 	/***
414 	 * return the object value from the database
415 	 * 
416 	 * @return the object
417 	 */
418 	protected Object getFieldObject() {
419 		Object fieldValueObj = null;
420 		ResultSetVector res = getParentForm().getResultSetVector();
421 
422 		if ((res != null) && (getField() != null)) {
423 			fieldValueObj = res.getFieldAsObject(getField().getName());
424 		} else {
425 			// try to get old value if we have an unbounded field!
426 			fieldValueObj = ParseUtil.getParameter(
427 					(HttpServletRequest) pageContext.getRequest(),
428 					getFormFieldName());
429 
430 			if (fieldValueObj == null) {
431 				// if we have an unbounded field and no old value then use
432 				// default!
433 				fieldValueObj = getDefaultValue();
434 			} else {
435 				fieldValueObj = (getEscaper() == null) ? fieldValueObj
436 						: getEscaper().unescapeHTML((String) fieldValueObj);
437 			}
438 		}
439 
440 		return fieldValueObj;
441 	}
442 
443 	/***
444 	 * fetches the value from the database. if no value is given, contents of
445 	 * attribute nullFieldValue is returned.
446 	 * 
447 	 * @return the field value
448 	 */
449 	protected String getFieldValue() {
450 		ResultSetVector rsv = getParentForm().getResultSetVector();
451 		String res = null;
452 
453 		if ((getField() != null) && (rsv != null)) {
454 			String[] s = rsv.getCurrentRow();
455 
456 			if (s != null) {
457 				res = rsv.getCurrentRow()[getField().getId()];
458 			}
459 		}
460 
461 		return res;
462 	}
463 
464 	/***
465 	 * DOCUMENT ME!
466 	 * 
467 	 * @return DOCUMENT ME!
468 	 */
469 	protected String getFormFieldDefaultValue() {
470 		if (defaultValue != null) {
471 			// default value defined by jsp-developer (provided via the "value"
472 			// attribute of the tag)
473 			return defaultValue;
474 		}
475 
476 		// fill out empty fields so that there are no plain field-syntax errors
477 		// on database operations...
478 		return typicalDefaultValue();
479 	}
480 
481 	/***
482 	 * generates the decoded name for the html-widget.
483 	 * 
484 	 * @return DOCUMENT ME!
485 	 */
486 	protected String getFormFieldName() {
487 
488 		StringBuffer buf = new StringBuffer();
489 
490 		if ((getParentForm().getTable() != null) && (getField() != null)) {
491 			String keyIndex = (getParentForm().isFooterReached()) ? (Constants.FIELDNAME_INSERTPREFIX + getParentForm()
492 					.getPositionPathCore())
493 					: getParentForm().getPositionPath();
494 			buf.append(Constants.FIELDNAME_PREFIX);
495 			buf.append(getParentForm().getTable().getId());
496 			buf.append("_");
497 			buf.append(keyIndex);
498 			buf.append("_");
499 			buf.append(getField().getId());
500 		} else {
501 			buf.append(fieldName);
502 		}
503 
504 		return buf.toString();
505 	}
506 
507 	/***
508 	 * Philip Grunikiewicz 2001-05-31 determinates value of the html-widget. In
509 	 * a jsp which contains many input fields, it may be desirable, in the event
510 	 * of an error, to redisplay input data. (instead of refreshing the fields
511 	 * from the DB) Currently dbforms implements this functionality with INSERT
512 	 * fields only. The following describes the changes I've implemented: - I've
513 	 * added a new attribute in the Form tag which sets the functionality
514 	 * (redisplayFieldsOnError=true/false) - I've modified the code below to
515 	 * handle the redisplay of previously posted information
516 	 * 
517 	 * @return DOCUMENT ME!
518 	 */
519 	protected String getFormFieldValue() {
520 		HttpServletRequest request = (HttpServletRequest) this.pageContext
521 				.getRequest();
522 		Vector errors = (Vector) request.getAttribute("errors");
523 		AbstractWebEvent we = getParentForm().getWebEvent();
524 
525 		// Are we in Update mode
526 		if (!getParentForm().isFooterReached()) {
527 			// Check if attribute 'redisplayFieldsOnError' has been set to true
528 			// and is this jsp displaying an error?
529 			if ((getParentForm().hasRedisplayFieldsOnErrorSet()
530 					&& (errors != null) && (errors.size() > 0))
531 					|| ((we != null) && EventType.EVENT_NAVIGATION_RELOAD
532 							.equals(we.getType()))) {
533 				// Yes - redisplay posted data
534 				String oldValue = ParseUtil.getParameter(request,
535 						getFormFieldName());
536 
537 				if (oldValue != null) {
538 					return oldValue;
539 				}
540 			}
541 
542 			return getFormattedFieldValue();
543 		} else {
544 			// the form field is in 'insert-mode'
545 			if (((we != null) && (EventType.EVENT_NAVIGATION_COPY.equals(we
546 					.getType())))) {
547 				String copyValue = ParseUtil.getParameter(request,
548 						getFormFieldNameForCopyEvent());
549 
550 				if (copyValue != null) {
551 					return copyValue;
552 				}
553 			}
554 
555 			if (((we != null) && EventType.EVENT_NAVIGATION_RELOAD.equals(we
556 					.getType()))
557 					|| ((errors != null) && (errors.size() > 0))) {
558 				String oldValue = ParseUtil.getParameter(request,
559 						getFormFieldName());
560 
561 				if (oldValue != null) {
562 					return oldValue;
563 				}
564 
565 				// Patch to reload checkbox, because when unchecked checkbox is
566 				// null
567 				// If unchecked, return anything different of
568 				// typicalDefaultValue() ...
569 				if (this instanceof DbCheckboxTag) {
570 					return typicalDefaultValue() + "_";
571 				}
572 			} 
573 			if (getField() == null) {
574 				String oldValue = ParseUtil.getParameter(request,
575 						getFormFieldName());
576 
577 				if (oldValue != null) {
578 					return oldValue;
579 				}
580 			}	
581 
582 			return getFormFieldDefaultValue();
583 		}
584 	}
585 
586 	/***
587 	 * fetches the value from the database. if no value is given, contents of
588 	 * attribute nullFieldValue is returned.
589 	 * 
590 	 * @return the field value
591 	 */
592 	protected String getFormattedFieldValue() {
593 		Object fieldValueObj = getFieldObject();
594 		String res;
595 
596 		if (fieldValueObj == null) {
597 			res = getNullFieldValue();
598 		} else {
599 			// if column object returned by database is of type
600 			// 'array of byte: byte[]' (which can happen in case
601 			// of eg. LONGVARCHAR columns), method toString would
602 			// just return a sort of String representation
603 			// of the array's address. So in this case it is
604 			// better to create a String using a corresponding
605 			// String constructor:
606 			if (fieldValueObj.getClass().isArray()
607 					&& "byte".equals(fieldValueObj.getClass()
608 							.getComponentType().toString())) {
609 				res = new String((byte[]) fieldValueObj);
610 			} else if (getField() != null) {
611 				switch (getField().getType()) {
612 				case FieldTypes.INTEGER:
613 				case FieldTypes.DOUBLE:
614 				case FieldTypes.FLOAT:
615 				case FieldTypes.NUMERIC:
616 				case FieldTypes.DATE:
617 				case FieldTypes.TIME:
618 				case FieldTypes.TIMESTAMP:
619 
620 					try {
621 						res = getFormatter().format(fieldValueObj);
622 					} catch (Exception e) {
623 						logCat.error("field type: " + getField().getType()
624 								+ "\n" + "object type: "
625 								+ fieldValueObj.getClass().getName() + "\n"
626 								+ "pattern: " + getPattern() + "\n"
627 								+ e.getMessage());
628 						res = fieldValueObj.toString();
629 					}
630 
631 					break;
632 
633 				case FieldTypes.BLOB:
634 				case FieldTypes.DISKBLOB:
635 				case FieldTypes.CHAR:
636 				case FieldTypes.VARCHAR:
637 				case FieldTypes.LONGVARCHAR:
638 				default:
639 					res = fieldValueObj.toString();
640 
641 					break;
642 				}
643 			} else {
644 				res = fieldValueObj.toString();
645 			}
646 		}
647 		// add support for custom formatting - neal katz
648 		res = customFormat(res);
649 		return res;
650 	}
651 
652 	/***
653 	 * DOCUMENT ME!
654 	 * 
655 	 * @return DOCUMENT ME!
656 	 */
657 	protected Locale getLocale() {
658 		return getParentForm().getLocale();
659 	}
660 
661 
662 	/***
663 	 * DOCUMENT ME!
664 	 * 
665 	 * @return DOCUMENT ME!
666 	 */
667 	protected DbFormTag getParentForm() {
668 		return parentForm;
669 	}
670 
671 	/***
672 	 * Just a shortcut for calling the escaper
673 	 * 
674 	 * @param html
675 	 *            string to escape
676 	 * 
677 	 * @return escaped string
678 	 */
679 	protected String escapeHTML(String html) {
680 		return (getEscaper() == null) ? html : getEscaper().escapeHTML(html);
681 	}
682 
683 	/***
684 	 * writes out the field value in hidden field _old
685 	 * 
686 	 * @return DOCUMENT ME!
687 	 */
688 	protected String renderOldValueHtmlInputField() {
689 		StringBuffer tagBuf = new StringBuffer();
690 		tagBuf.append("<input type=\"hidden\" name=\"");
691 		tagBuf.append(Constants.FIELDNAME_OLDVALUETAG + getFormFieldName());
692 		tagBuf.append("\" value=\"");
693 
694 		if (!getParentForm().isFooterReached()) {
695 			tagBuf.append(escapeHTML(getFormattedFieldValue()));
696 		} else {
697 			tagBuf.append(escapeHTML(getFormFieldDefaultValue()));
698 		}
699 
700 		tagBuf.append("\" />");
701 
702 		return tagBuf.toString();
703 	}
704 
705 	/***
706 	 * writes out the current used format to the page
707 	 * 
708 	 * @return DOCUMENT ME!
709 	 * 
710 	 * @throws JspException
711 	 */
712 	protected String renderPatternHtmlInputField() {
713 		StringBuffer tagBuf = new StringBuffer();
714 		String ppattern = getPattern();
715 
716 		if (!Util.isNull(ppattern)) {
717 			tagBuf.append("<input type=\"hidden\" name=\"");
718 			tagBuf.append(Constants.FIELDNAME_PATTERNTAG + getFormFieldName());
719 			tagBuf.append("\" value=\"");
720 			tagBuf.append(ppattern);
721 			tagBuf.append("\" />");
722 		}
723 
724 		return tagBuf.toString();
725 	}
726 
727 	/***
728 	 * DOCUMENT ME!
729 	 * 
730 	 * @return DOCUMENT ME!
731 	 */
732 	protected String typicalDefaultValue() {
733 		// 20030113-HKK: Change to use format too
734 		String res = "";
735 
736 		if (getField() != null) {
737 			switch (field.getType()) {
738 			case org.dbforms.config.FieldTypes.INTEGER:
739 			case org.dbforms.config.FieldTypes.NUMERIC:
740 			case org.dbforms.config.FieldTypes.DOUBLE:
741 			case org.dbforms.config.FieldTypes.FLOAT:
742 
743 				try {
744 					res = getFormatter().format(new Double(0));
745 				} catch (Exception e) {
746 					res = "0";
747 				}
748 
749 			// in all other cases we just leave the formfield empty
750 			}
751 		}
752 
753 		return res;
754 	}
755 
756 	/***
757 	 * writes out all hidden fields for the input fields
758 	 */
759 	protected void writeOutSpecialValues() throws JspException {
760 		try {
761 			pageContext.getOut().write(renderOldValueHtmlInputField());
762 		} catch (java.io.IOException ioe) {
763 			throw new JspException("IO Error: " + ioe.getMessage());
764 		}
765 	}
766 
767 	/***
768 	 * generates the decoded name for the html-widget in the case of copy
769 	 * events.
770 	 * 
771 	 * @return DOCUMENT ME!
772 	 */
773 	private String getFormFieldNameForCopyEvent() {
774 		boolean footerReached = getParentForm().isFooterReached();
775 		getParentForm().setFooterReached(false);
776 
777 		String name = getFormFieldName();
778 		getParentForm().setFooterReached(footerReached);
779 
780 		return name;
781 	}
782 
783 	/***
784 	 * Gets the nullFieldValue attribute of the DbLabelTag object
785 	 * 
786 	 * @return The nullFieldValue value
787 	 */
788 	private String getNullFieldValue() {
789 		String res = nullFieldValue;
790 
791 		if (res == null) {
792 			res = MessageResourcesInternal.getMessage("dbforms.nodata",
793 					getLocale());
794 		}
795 
796 		// Resolve message if captionResource=true in the Form Tag
797 		if ((getParentForm() != null)
798 				&& getParentForm().hasCaptionResourceSet()) {
799 			res = MessageResources.getMessage(res, getLocale(), res);
800 		}
801 
802 		/***
803 		 * Philip Grunikiewicz 2003-12-04 The data being return has a value of
804 		 * null. The developer has not specified a substitute. So instead of
805 		 * crashing, lets display an empty string!
806 		 */
807 		if (res == null) {
808 			res = "";
809 		}
810 
811 		return res;
812 	}
813 	
814 	
815 }