001: /*
002: * Copyright 2001-2006 C:1 Financial Services GmbH
003: *
004: * This software is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License Version 2.1, as published by the Free Software Foundation.
007: *
008: * This software is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011: * Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public
014: * License along with this library; if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
016: */
017:
018: package de.finix.contelligent.render;
019:
020: import java.io.CharArrayWriter;
021: import java.io.IOException;
022: import java.io.Writer;
023: import java.text.ParseException;
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.Collections;
027: import java.util.HashMap;
028: import java.util.HashSet;
029: import java.util.Iterator;
030: import java.util.List;
031: import java.util.Map;
032: import java.util.Set;
033:
034: import javax.servlet.http.HttpServletRequest;
035: import javax.servlet.http.HttpUtils;
036:
037: import de.finix.contelligent.CallData;
038: import de.finix.contelligent.Component;
039: import de.finix.contelligent.ComponentManager;
040: import de.finix.contelligent.ComponentNotFoundException;
041: import de.finix.contelligent.ComponentPath;
042: import de.finix.contelligent.Container;
043: import de.finix.contelligent.HTTPCallData;
044: import de.finix.contelligent.core.AbstractCallDataImpl;
045: import de.finix.contelligent.exception.CircularRenderException;
046: import de.finix.contelligent.exception.ComponentPersistenceException;
047: import de.finix.contelligent.exception.ContelligentException;
048: import de.finix.contelligent.exception.ContelligentExceptionID;
049: import de.finix.contelligent.logging.LoggingService;
050:
051: public class Template {
052: final static org.apache.log4j.Logger log = LoggingService
053: .getLogger(Template.class);
054:
055: final static public String FINIX_TAG_PREFIX = "<finix:";
056:
057: final static private int FINIX_TAG_PREFIX_LENGTH = FINIX_TAG_PREFIX
058: .length();
059:
060: final static public String RENDER_IDENTIFIER = "render "; // XXX: should
061:
062: // we match any
063: // whitespace?
064:
065: final static private int RENDER_LENGTH = RENDER_IDENTIFIER.length();
066:
067: final static public String THIS_PATH = ".";
068:
069: final static public String PATH_ATTRIBUTE = "path";
070:
071: final static public String SUBSTITUTION_PREFIX = "{{";
072:
073: final static public String SUBSTITUTION_POSTFIX = "}}";
074:
075: final static public int EVAL_FINAL = 1;
076:
077: final static public int EVAL_STATIC = 2;
078:
079: final static public int EVAL_DYNAMIC = 4;
080:
081: private String text;
082:
083: private List precompiledTemplate, optimizedTemplate;
084:
085: public Template(String text) throws ContelligentException {
086: setText(text);
087: }
088:
089: public void setText(String text) throws ContelligentException {
090: this .text = text;
091: precompiledTemplate = precompileTemplate(text);
092: }
093:
094: public String getText() {
095: return text;
096: }
097:
098: public void setPrecompiledTemplate(List precompiledTemplate) {
099: this .precompiledTemplate = precompiledTemplate;
100: }
101:
102: public List getPrecompiledTemplate() throws ContelligentException {
103: return precompiledTemplate;
104: }
105:
106: public List precompileTemplate(String template)
107: throws ContelligentException {
108: final boolean debugEnabled = log.isDebugEnabled();
109: // replace internal links with finix tags...
110: int lastPosition = 0, currentPosition = 0;
111: int close = -1;
112: List precompiledTemplate = new ArrayList();
113: while ((currentPosition = template.indexOf(FINIX_TAG_PREFIX,
114: lastPosition)) >= 0) {
115: if (debugEnabled)
116: log
117: .debug("precompileTemplate() - ... found tag-prefix '"
118: + FINIX_TAG_PREFIX
119: + "' at position "
120: + currentPosition + " ...");
121: precompiledTemplate.add(new PrecompiledTemplateFragment(
122: template.substring(lastPosition, currentPosition)));
123: lastPosition = currentPosition;
124: currentPosition += FINIX_TAG_PREFIX_LENGTH;
125: if (template.startsWith(RENDER_IDENTIFIER, currentPosition)) {
126: if (debugEnabled)
127: log
128: .debug("precompileTemplate() - ... identified tag '"
129: + RENDER_IDENTIFIER
130: + "' at position "
131: + currentPosition + " ...");
132: currentPosition += RENDER_LENGTH;
133: close = template.indexOf(">", currentPosition);
134: if (close != -1) {
135: if (template.charAt(close - 1) == '/') {
136: precompileRenderTag(precompiledTemplate,
137: template, currentPosition, close - 1);
138: } else {
139: // normally we would have to check for a closing
140: // </finix:..> tag in this case // TODO: warn or debug?
141: log
142: .debug("precompileTemplate() - normally a 'finix:render' tag MUST end with '/>' ...");
143: precompileRenderTag(precompiledTemplate,
144: template, currentPosition, close);
145: }
146: }
147: } else {
148: // rat: Removed. This used to report the wrong string on errors.
149: // currentPosition += RENDER_LENGTH;
150: close = template.indexOf(">", currentPosition);
151: if (template.length() >= currentPosition + 10) {
152: log
153: .warn("precompileTemplate() - ignoring unknown tag at position "
154: + currentPosition
155: + ", starts with ["
156: + template.substring(
157: currentPosition,
158: currentPosition + 10)
159: + "]!");
160: } else {
161: log
162: .warn("precompileTemplate() - ignoring unknown tag at position "
163: + currentPosition
164: + ", near end of content!");
165: }
166: }
167: if (close == -1) {
168: log
169: .error("precompileTemplate() - parse-error at position "
170: + currentPosition
171: + ", tag has to end with either > or /> ");
172: throw new ComponentPersistenceException(
173: ContelligentExceptionID.component_unclosedRenderTag);
174: }
175: currentPosition = (close + 1);
176: lastPosition = currentPosition;
177: }
178:
179: if (lastPosition > template.length()) {
180: log
181: .error("precompileTemplate() - last position is greater than lenght!");
182: } else {
183: precompiledTemplate
184: .add(new PrecompiledTemplateFragment(template
185: .substring(lastPosition, template.length())));
186: }
187: return precompiledTemplate;
188: }
189:
190: private void precompileRenderTag(List precompiledTemplate,
191: String template, int start, int end)
192: throws ContelligentException {
193: try {
194: String path = getAttribute(template.substring(start, end),
195: PATH_ATTRIBUTE);
196: if (path.trim().length() == 0) {
197: throw new ContelligentException(
198: "Parse-error: tag contains empty path");
199: }
200: ComponentPath componentPath;
201: Map parameterMap = null;
202: int questionMark = path.indexOf('?');
203: String pathString = path;
204:
205: if (questionMark != -1) {
206: pathString = path.substring(0, questionMark);
207: String parameterString = path
208: .substring(questionMark + 1);
209: parameterMap = HttpUtils
210: .parseQueryString(parameterString);
211: }
212: precompiledTemplate.add(createFragment(new ComponentPath(
213: pathString), parameterMap));
214: } catch (ParseException exception) {
215: throw new ComponentPersistenceException(
216: ContelligentExceptionID.component_invalidRenderTag,
217: new Object[] { template.substring(start, end) });
218: }
219: }
220:
221: /**
222: * Overwrite to create specific precompiled template fragment for render
223: * tag.
224: *
225: * @param componentPath
226: * @param parameterMap
227: * @return
228: */
229: protected PrecompiledTemplateFragment createFragment(
230: ComponentPath componentPath, Map parameterMap) {
231: return new PrecompiledTemplateFragment(componentPath,
232: parameterMap);
233: }
234:
235: /**
236: * Overwrite to create specific precompiled template fragment for text.
237: */
238: protected PrecompiledTemplateFragment createFragment(String text) {
239: return new PrecompiledTemplateFragment(text);
240: }
241:
242: public List getOptimizedTemplate(int options, Component component,
243: CallData callData) throws IOException {
244: if (optimizedTemplate == null) {
245: optimizedTemplate = new ArrayList();
246: for (Iterator i = precompiledTemplate.iterator(); i
247: .hasNext();) {
248: optimizedTemplate.add(((PrecompiledTemplateFragment) i
249: .next()).getOptimizedTemplateFragment(options,
250: component, callData));
251: }
252: // concat all text fragments that are not separated by render-tags
253: for (Iterator i = precompiledTemplate.iterator(); i
254: .hasNext();) {
255: PrecompiledTemplateFragment fragment = (PrecompiledTemplateFragment) i
256: .next();
257: }
258: }
259: return optimizedTemplate;
260: }
261:
262: static String getAttribute(String tagTorso, String attributeName)
263: throws ParseException {
264: int current = 0;
265: int separatorIndex = -1;
266:
267: while ((separatorIndex = tagTorso.indexOf('=', current)) > -1) {
268: String attrName = tagTorso.substring(current,
269: separatorIndex).trim();
270: current = separatorIndex;
271:
272: // skip whitespaces:
273: while (tagTorso.length() > current
274: && Character.isWhitespace(tagTorso
275: .charAt(current++)))
276: ;
277:
278: if (tagTorso.charAt(current) != '\"'
279: && tagTorso.charAt(current) != '\'') {
280: log
281: .error("getAttributeMap() - tag evaluation error: attribute values have to be included in quotes [\" or \'] for attribute "
282: + attrName);
283: throw new ParseException(
284: "tag evaluation error: attribute values have to be included in quotes [\" or \'] for attribute "
285: + attrName, current);
286: }
287: current++;
288: separatorIndex = tagTorso.indexOf("\"", current);
289: if (separatorIndex == -1) {
290: separatorIndex = tagTorso.indexOf("\'", current);
291: }
292: if (separatorIndex < 0) {
293: log
294: .error("tag evaluation error: attribute has to be quoted at the end [\" or \']");
295: throw new ParseException(
296: "tag evaluation error: attribute has to be quoted at the end [\" or \']",
297: current);
298: }
299: String attrValue = tagTorso.substring(current,
300: separatorIndex).trim();
301:
302: if (log.isDebugEnabled()) {
303: log.debug("getAttributeMap() - found attribute '"
304: + attrName + "=\"" + attrValue + "\"' ...");
305: }
306: if (attrName.equals(attributeName)) {
307: return attrValue;
308: }
309: current = separatorIndex + 1;
310: }
311: log.error("tag evaluation error: attribute '" + attributeName
312: + "' is missing");
313: throw new ParseException("tag evaluation error: attribute '"
314: + attributeName + "' is missing in " + tagTorso,
315: current);
316: }
317:
318: static Map getAttributeMap(String tagTorso) throws ParseException {
319: Map attributes = new HashMap();
320: int current = 0;
321: int separatorIndex = -1;
322:
323: while ((separatorIndex = tagTorso.indexOf('=', current)) > -1) {
324: String attrName = tagTorso.substring(current,
325: separatorIndex).trim();
326: current = separatorIndex;
327:
328: // skip whitespaces:
329: while (tagTorso.length() > current
330: && Character.isWhitespace(tagTorso
331: .charAt(current++)))
332: ;
333:
334: if (tagTorso.charAt(current) != '\"'
335: && tagTorso.charAt(current) != '\'') {
336: log
337: .error("getAttributeMap() - tag evaluation error: attribute values have to be included in quotes [\" or \'] for attribute "
338: + attrName);
339: throw new ParseException(
340: "tag evaluation error: attribute values have to be included in quotes [\" or \'] for attribute "
341: + attrName, current);
342: }
343: current++;
344: separatorIndex = tagTorso.indexOf("\"", current);
345: if (separatorIndex == -1) {
346: separatorIndex = tagTorso.indexOf("\'", current);
347: }
348: if (separatorIndex < 0) {
349: log
350: .error("tag evaluation error: attribute has to be quoted at the end [\" or \']");
351: throw new ParseException(
352: "tag evaluation error: attribute has to be quoted at the end [\" or \']",
353: current);
354: }
355: String attrValue = tagTorso.substring(current,
356: separatorIndex).trim();
357:
358: if (log.isDebugEnabled()) {
359: log.debug("getAttributeMap() - found attribute '"
360: + attrName + "=\"" + attrValue + "\"' ...");
361: }
362: attributes.put(attrName, attrValue);
363: current = separatorIndex;
364: }
365: return attributes;
366: }
367:
368: public void write(Writer writer, Component component,
369: CallData callData) throws ContelligentException,
370: IOException {
371: List templateToWrite = precompiledTemplate;
372: if (optimizedTemplate != null) {
373: templateToWrite = optimizedTemplate;
374: }
375: for (Iterator i = templateToWrite.iterator(); i.hasNext();) {
376: ((PrecompiledTemplateFragment) i.next()).write(writer,
377: component, callData);
378: }
379: }
380:
381: private static Component resolveComponent(Component component,
382: ComponentPath componentPath, CallData callData)
383: throws ComponentNotFoundException {
384: // loadType component and check if it should be inlined...
385: ComponentManager manager = callData.getActualManager();
386: Component resolvedComponent = null;
387:
388: if (componentPath.isAbsolute()) {
389: resolvedComponent = manager.getComponent(componentPath,
390: callData);
391: } else if (componentPath.toString().equals(THIS_PATH)) {
392: return component;
393: } else {
394: if (component != null && component instanceof Container) {
395: resolvedComponent = manager.getSubcomponent(
396: (Container) component, componentPath, callData);
397: } else {
398: log
399: .error("resolveComponent() - evaluation of relative path '"
400: + componentPath
401: + "' impossible without a component!");
402: throw new ComponentNotFoundException(
403: ContelligentExceptionID.component_notFound_relativePathWithoutParent,
404: componentPath);
405: }
406: }
407: return resolvedComponent;
408: }
409:
410: /**
411: * Returns a collection of ComponentPath instances which may be relative or
412: * absolute where each path represents the path attribute of a
413: * {@link #RENDER_IDENTIFIER render tag}. <BR>
414: * Note that the paths may still contain category tokens!
415: */
416: public Collection getPathsOfRelationTargets() {
417: Collection relationSources = new HashSet();
418: Iterator templates = precompiledTemplate.iterator();
419: while (templates.hasNext()) {
420: PrecompiledTemplateFragment fragment = (PrecompiledTemplateFragment) templates
421: .next();
422: if (fragment.getType() == PrecompiledTemplateFragment.RENDER_TAG) {
423: ComponentPath componentPath = fragment
424: .getComponentPath();
425: if (!componentPath.toString().equals(THIS_PATH)) { // skip
426: // THIS_PATH
427: // (='.') !
428: relationSources.add(componentPath);
429: }
430: } // else nothing to do: the fragment does not contain any
431: // relation.
432: }
433: return relationSources;
434: }
435:
436: public static class PrecompiledTemplateFragment implements
437: Cloneable {
438: public final static int TEXT_FRAGMENT = 0;
439:
440: public final static int TEXT_FRAGMENT_SUBSTITUTE = 2;
441:
442: public final static int RENDER_TAG = 1;
443:
444: private int type;
445:
446: private String textFragment;
447:
448: private Map parameterMap;
449:
450: private ComponentPath componentPath;
451:
452: public PrecompiledTemplateFragment(String textFragment) {
453: boolean substitute = false;
454: if (textFragment != null) {
455: int pospre = textFragment.indexOf(SUBSTITUTION_PREFIX);
456: int pospost = textFragment
457: .indexOf(SUBSTITUTION_POSTFIX);
458: if ((pospre >= 0) && (pospost >= 0)) {
459: substitute = true;
460: log.debug("Fragment will be posprocessed.");
461: }
462: }
463: // wenn im text Zeichen auftachen, die anzeigen, dass unter
464: // Umständen Textbaustein wie z.b.
465: // {{request.scheme}} auftauchen, dann wird dieses Fragment als
466: // SUBSTITUTE gekennzeichnet.
467: // Beim rendern wird der Text vor der Ausgabe dann nochmal
468: // zusätzlich geparsed.
469: this .type = substitute ? TEXT_FRAGMENT_SUBSTITUTE
470: : TEXT_FRAGMENT;
471: this .textFragment = textFragment;
472: }
473:
474: public PrecompiledTemplateFragment(ComponentPath componentPath) {
475: this .type = RENDER_TAG;
476: this .componentPath = componentPath;
477: this .parameterMap = null;
478: }
479:
480: public PrecompiledTemplateFragment(ComponentPath componentPath,
481: Map parameterMap) {
482: this .type = RENDER_TAG;
483: this .componentPath = componentPath;
484: if (parameterMap != null) {
485: this .parameterMap = Collections
486: .unmodifiableMap(parameterMap);
487: }
488: }
489:
490: public String getTextFragment() {
491: return textFragment;
492: }
493:
494: /**
495: * Get the type of the fragment.
496: *
497: * @return the type of the fragment.
498: */
499: public int getType() {
500: return type;
501: }
502:
503: /**
504: * Get the component path of a relation target specified by a render tag
505: * type fragment.
506: *
507: * @return the component path of a relation target.
508: */
509: public ComponentPath getComponentPath() {
510: return componentPath;
511: }
512:
513: /**
514: * /** Set the component path of a relation target specified by a render
515: * tag type fragment.
516: *
517: * @param componentPath
518: * the component path of a relation target.
519: */
520: public void setComponentPath(ComponentPath componentPath) {
521: this .componentPath = componentPath;
522: }
523:
524: public PrecompiledTemplateFragment getOptimizedTemplateFragment(
525: int options, Component component, CallData callData)
526: throws IOException {
527: PrecompiledTemplateFragment fragment = (PrecompiledTemplateFragment) this
528: .clone();
529: if (type == RENDER_TAG) {
530: try {
531: Component resolvedComponent = resolveComponent(
532: component, componentPath, callData);
533: if (resolvedComponent instanceof Renderable) {
534: if ((resolvedComponent.getComponentContext()
535: .isFinal() && ((options & EVAL_FINAL) != 0))
536: || (!resolvedComponent.isDynamic() && ((options & EVAL_STATIC) != 0))) {
537: Renderer renderer = ((Renderable) resolvedComponent)
538: .getRenderer();
539: CharArrayWriter writer = new CharArrayWriter(
540: 16384);
541: renderer.render(writer, parameterMap,
542: callData);
543: fragment = new PrecompiledTemplateFragment(
544: writer.toString());
545: }
546: } else {
547: log
548: .error("optimize() - try to optimize non renderable component '"
549: + componentPath + "'!");
550: }
551: } catch (ComponentNotFoundException exception) {
552: log.error("optimize() - component '"
553: + componentPath + "' not found!");
554: } catch (ContelligentException exception) {
555: log
556: .error("optimize() - could not optimize fragment!");
557: }
558: }
559: return fragment;
560: }
561:
562: public void write(Writer writer, Component component,
563: CallData callData) throws ContelligentException,
564: IOException {
565: if (type == TEXT_FRAGMENT) {
566: if (log.isDebugEnabled()) {
567: log.debug("write() - rendering text fragment: '"
568: + textFragment + "' ...");
569: }
570: renderText(writer);
571: } else if (type == TEXT_FRAGMENT_SUBSTITUTE) {
572: if (log.isDebugEnabled()) {
573: log
574: .debug("write() - rendering text fragment (postprocess): '"
575: + textFragment + "' ...");
576: }
577: renderTextSubstitute(writer, callData);
578: } else if (type == RENDER_TAG) {
579: if (log.isDebugEnabled()) {
580: log.debug("write() - rendering component: '"
581: + componentPath + "' ...");
582: }
583: renderTag(component, componentPath, parameterMap,
584: callData, writer);
585: }
586: }
587:
588: public Object clone() {
589: PrecompiledTemplateFragment fragment = null;
590: if (type == TEXT_FRAGMENT) {
591: fragment = new PrecompiledTemplateFragment(new String(
592: textFragment));
593: } else if (type == RENDER_TAG) {
594: fragment = new PrecompiledTemplateFragment(
595: componentPath);
596: if (parameterMap != null) {
597: fragment.parameterMap = new HashMap(parameterMap);
598: }
599: }
600: return fragment;
601: }
602:
603: /**
604: * Resolve component with absolute path or path relative to given
605: * component.
606: *
607: * @param component
608: * @param componentPath
609: * @param callData
610: * @return
611: * @throws ComponentNotFoundException
612: */
613: protected Component resolveComponent(Component component,
614: ComponentPath componentPath, CallData callData)
615: throws ComponentNotFoundException {
616: return Template.resolveComponent(component, componentPath,
617: callData);
618: }
619:
620: /**
621: * Render render tag. Overwrite to change rendering behaviour.
622: *
623: * @param component
624: * @param componentPath
625: * @param parameterMap
626: * @param callData
627: * @param writer
628: * @throws ContelligentException
629: * @throws IOException
630: */
631: protected void renderTag(Component component,
632: ComponentPath componentPath, Map parameterMap,
633: CallData callData, Writer writer)
634: throws ContelligentException, IOException {
635:
636: boolean pushed = false;
637: Map oldCategoryMap = callData.getCategoryMap();
638: Map oldParameterMap = callData.getParameters();
639: HashMap newCategoryMap = null;
640: HashMap newParameterMap = null;
641: AbstractCallDataImpl cImpl = (AbstractCallDataImpl) callData;
642:
643: try {
644: // switch categories, if applicable and copy parameters
645: // to callData parameterMap
646: // (Moved ahead before the resolve in 9.1 to allow
647: // CurrentPage to respond to the new parameters)
648: if (parameterMap != null) {
649: Iterator it = parameterMap.keySet().iterator();
650: while (it.hasNext()) {
651: String paramName = (String) it.next();
652: Object o = parameterMap.get(paramName);
653: if (oldCategoryMap.containsKey(paramName)) {
654: if (newCategoryMap == null) {
655: newCategoryMap = new HashMap(
656: oldCategoryMap);
657: }
658: if (o instanceof String[]) {
659: newCategoryMap.put(paramName,
660: ((String[]) o)[0]);
661: } else if (o instanceof String) {
662: newCategoryMap.put(paramName,
663: (String) o);
664: }
665: }
666: if (newParameterMap == null) {
667: newParameterMap = new HashMap(
668: oldParameterMap);
669: }
670: newParameterMap.put(paramName, o);
671: }
672: if (newCategoryMap != null) {
673: cImpl.setCategoryMap(newCategoryMap);
674: }
675: if (newParameterMap != null) {
676: cImpl.setParameters(newParameterMap);
677: }
678: }
679:
680: // Extremely verbose logging of parameter maps for each
681: // render call. Might be useful later on...
682: if (log.isDebugEnabled()) {
683: StringBuffer sb = new StringBuffer();
684: sb
685: .append("\n--------------------------\nRendering: "
686: + component.getComponentContext()
687: .getPath()
688: + "\nLocal parameterMap:\n");
689: if (parameterMap != null) {
690: Set keySet = parameterMap.keySet();
691: if (keySet != null) {
692: Iterator keys = keySet.iterator();
693: while (keys.hasNext()) {
694: String key = (String) keys.next();
695: String value = "";
696: Object o = parameterMap.get(key);
697: if (o instanceof String[]) {
698: value = ((String[]) o)[0];
699: } else if (o instanceof String) {
700: value = (String) o;
701: }
702: sb.append(key + " / " + value + "\n");
703: }
704: }
705: }
706: sb.append("\nInherited parameterMap:\n");
707: Map params = callData.getParameters();
708: if (params != null) {
709: Set keySet = params.keySet();
710: if (keySet != null) {
711: Iterator keys = keySet.iterator();
712: while (keys.hasNext()) {
713: String key = (String) keys.next();
714: String value = "";
715: Object o = params.get(key);
716: if (o instanceof String[]) {
717: value = ((String[]) o)[0];
718: } else if (o instanceof String) {
719: value = (String) o;
720: }
721: sb.append(key + " / " + value + "\n");
722: }
723: }
724: }
725: log.debug(sb.toString());
726: }
727: // End of parameter/category setup
728:
729: Component resolvedComponent = resolveComponent(
730: component, componentPath, callData);
731: if (resolvedComponent instanceof Renderable) {
732: ComponentPath realPath = resolvedComponent
733: .getComponentContext().getPath();
734: boolean alreadyRendered = callData
735: .checkRenderStack(realPath);
736: if (alreadyRendered) {
737: throw new CircularRenderException();
738: }
739: Renderer renderer = ((Renderable) resolvedComponent)
740: .getRenderer();
741: callData.pushRenderStack(realPath);
742: pushed = true;
743:
744: // Actual passing of control happens here
745: renderer.render(writer, parameterMap, callData);
746:
747: } else {
748: log
749: .error("write() - can not render non-renderable component '"
750: + resolvedComponent + "'!");
751: throw new ContelligentException("Component '"
752: + resolvedComponent
753: + "' is not renderable!");
754: }
755: } catch (ComponentNotFoundException exception) {
756: Component blueprint;
757: ComponentPath blueprintPath = component
758: .getComponentContext().getType()
759: .getBlueprintPath();
760: if (blueprintPath == null
761: || component.getComponentContext().getPath()
762: .equals(blueprintPath)) {
763: blueprint = component;
764: } else {
765: ComponentManager manager = callData
766: .getActualManager();
767: blueprint = manager.getComponent(blueprintPath,
768: callData);
769: }
770: log.warn("render() - component '" + componentPath
771: + "' not found, checking if blueprint has it!");
772: // check if component exists at blueprint definition
773: try {
774: Component resolvedComponent = resolveComponent(
775: blueprint, componentPath, callData);
776: if (resolvedComponent instanceof Renderable) {
777: Renderer renderer = ((Renderable) resolvedComponent)
778: .getRenderer();
779: renderer.render(writer, parameterMap, callData);
780: } else {
781: log
782: .error("write() - can not render non-renderable component '"
783: + resolvedComponent + "'!");
784: throw new ContelligentException("Component '"
785: + resolvedComponent
786: + "' is not renderable!");
787: }
788: } catch (ComponentNotFoundException e) {
789: log.error("render() - component '" + componentPath
790: + "' not found (even not at blueprint)!");
791: }
792: } finally {
793: // always restore the old category map
794: if (newCategoryMap != null) {
795: cImpl.setCategoryMap(oldCategoryMap);
796: }
797: if (newParameterMap != null) {
798: cImpl.setParameters(oldParameterMap);
799: }
800: }
801:
802: // The pop call is intentionally not in the finally block,
803: // because the decision what to do with the stack
804: // is the responsibility of whoever catches the
805: // exception.
806: if (pushed) {
807: callData.popRenderStack();
808: }
809:
810: }
811:
812: /**
813: * Render constant text.
814: *
815: * @param writer
816: * @throws IOException
817: */
818: protected void renderText(Writer writer) throws IOException {
819: writer.write(textFragment);
820: }
821:
822: /**
823: * Dieser Sonderfall von renderText parsed den Text vor der Ausgabe
824: * nochmal. Dadurch können spezielle Platzhalter im Text wie z.B.
825: * {{request.scheme}} ersetzt werden. Derzeit wird nur dieser
826: * Platzhalter ersetzt. Geplant ist über diesen Mechanismus auch andere
827: * Platzhalter zu ermöglichen-
828: */
829: protected void renderTextSubstitute(Writer writer,
830: CallData callData) throws IOException {
831: String substituted = substitute(textFragment, callData);
832: writer.write(substituted);
833: }
834:
835: final static String SCHEME_PARAMETER = "request.scheme";
836:
837: final static String SCHEME_PATTERN = SUBSTITUTION_PREFIX
838: + SCHEME_PARAMETER + SUBSTITUTION_POSTFIX;
839:
840: /**
841: * Ersetzungsfunktion für Platzhalter. Derzeit wird nur
842: * {{request.scheme}} ersetzt.
843: */
844: private String substitute(String content, CallData callData) {
845: StringBuffer ret = new StringBuffer();
846: int to = 0;
847: int from = 0;
848: try {
849: if (callData instanceof HTTPCallData) {
850:
851: HTTPCallData hc = (HTTPCallData) callData;
852:
853: HttpServletRequest request = hc
854: .getHttpServletRequest();
855: String scheme_req = request.getScheme();
856: String scheme_hdr = request
857: .getHeader(SCHEME_PARAMETER);
858: String scheme_prm = callData
859: .getParameter(SCHEME_PARAMETER);
860: String scheme = null;
861: if (scheme_prm != null) {
862: scheme = scheme_prm;
863: } else if (scheme_hdr != null) {
864: scheme = scheme_hdr;
865: } else {
866: scheme = scheme_req;
867: }
868:
869: to = content.indexOf(SCHEME_PATTERN);
870: while (to >= 0) {
871: ret.append(content.substring(from, to));
872: ret.append(scheme);
873:
874: String test = ret.toString();
875: from = to + SCHEME_PATTERN.length();
876: to = content.indexOf(SCHEME_PATTERN, to + 1);
877: }
878: ret.append(content.substring(from));
879: return ret.toString();
880: }
881: } catch (Exception e) {
882: log.error("EXC: " + e);
883: log.debug("TRACE: ", e);
884: }
885: // wenn irgendwas schiefgeht, wird der content 1:1 geliefert
886: return content;
887: }
888:
889: } // class PrecompiledTemplateFragment
890: }
|