001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.jsp.java;
031:
032: import com.caucho.jsp.JspParseException;
033: import com.caucho.jsp.TagInstance;
034: import com.caucho.vfs.WriteStream;
035: import com.caucho.xml.QName;
036:
037: import java.io.IOException;
038: import java.util.ArrayList;
039:
040: /**
041: * Special generator for a JSTL c:forEach tag.
042: */
043: public class JstlCoreForEach extends JstlNode {
044: private static final QName VAR = new QName("var");
045: private static final QName VAR_STATUS = new QName("varStatus");
046:
047: private static final QName ITEMS = new QName("items");
048: private static final QName BEGIN = new QName("begin");
049: private static final QName END = new QName("end");
050: private static final QName STEP = new QName("step");
051:
052: private String _var;
053: private String _varStatus;
054:
055: private String _items;
056: private JspAttribute _itemsAttr;
057:
058: private String _begin;
059: private JspAttribute _beginAttr;
060:
061: private String _end;
062: private JspAttribute _endAttr;
063:
064: private String _step;
065: private JspAttribute _stepAttr;
066:
067: private boolean _isInteger;
068: private int _depth;
069: private String _tagVar;
070:
071: private TagInstance _tag;
072:
073: /**
074: * Adds an attribute.
075: */
076: public void addAttribute(QName name, String value)
077: throws JspParseException {
078: if (VAR.equals(name))
079: _var = value;
080: else if (VAR_STATUS.equals(name))
081: _varStatus = value;
082: else if (ITEMS.equals(name)) {
083: _items = value;
084: _attributeNames.add(name);
085: _attributeValues.add(value);
086: } else if (BEGIN.equals(name))
087: _begin = value;
088: else if (END.equals(name))
089: _end = value;
090: else if (STEP.equals(name))
091: _step = value;
092: else
093: throw error(L.l("'{0}' is an unknown attribute for <{1}>.",
094: name.getName(), getTagName()));
095: }
096:
097: /**
098: * Adds an attribute.
099: */
100: public void addAttribute(QName name, JspAttribute value)
101: throws JspParseException {
102: if (ITEMS.equals(name))
103: _itemsAttr = value;
104: else if (BEGIN.equals(name))
105: _beginAttr = value;
106: else if (END.equals(name))
107: _endAttr = value;
108: else if (STEP.equals(name))
109: _stepAttr = value;
110: else
111: throw error(L.l(
112: "'{0}' is an unknown jsp:attribute for <{1}>.",
113: name.getName(), getTagName()));
114: }
115:
116: /**
117: * Returns true if the tag has scripting values.
118: */
119: public boolean hasScripting() {
120: return (super .hasScripting() || hasScripting(_items)
121: || hasScripting(_itemsAttr) || hasScripting(_begin)
122: || hasScripting(_beginAttr) || hasScripting(_end)
123: || hasScripting(_endAttr) || hasScripting(_step) || hasScripting(_stepAttr));
124: }
125:
126: /**
127: * Returns true for an integer forEach.
128: */
129: public boolean isInteger() {
130: return _items == null && _itemsAttr == null;
131: }
132:
133: public TagInstance getTag() {
134: return _tag;
135: }
136:
137: /**
138: * Returns the tag name for the current tag.
139: */
140: public String getCustomTagName() {
141: if (_tag == null)
142: return null;
143: else
144: return _tag.getId();
145: }
146:
147: /**
148: * Returns true for a simple tag.
149: */
150: public boolean isSimpleTag() {
151: return false;
152: }
153:
154: /**
155: * Generates the XML text representation for the tag validation.
156: *
157: * @param os write stream to the generated XML.
158: */
159: public void printXml(WriteStream os) throws IOException {
160: os.print("<c:forEach");
161:
162: if (_itemsAttr != null) {
163: os.print(" items=\"");
164: _itemsAttr.printXml(os);
165: os.print("\"");
166: } else if (_items != null) {
167: os.print(" items=\"");
168: printXmlText(os, _items);
169: os.print("\"");
170: }
171:
172: if (_beginAttr != null) {
173: os.print(" begin=\"");
174: _beginAttr.printXml(os);
175: os.print("\"");
176: } else if (_begin != null) {
177: os.print(" begin=\"");
178: printXmlText(os, _begin);
179: os.print("\"");
180: }
181:
182: if (_endAttr != null) {
183: os.print(" end=\"");
184: _endAttr.printXml(os);
185: os.print("\"");
186: } else if (_end != null) {
187: os.print(" end=\"");
188: printXmlText(os, _end);
189: os.print("\"");
190: }
191:
192: if (_stepAttr != null) {
193: os.print(" step=\"");
194: _stepAttr.printXml(os);
195: os.print("\"");
196: } else if (_step != null) {
197: os.print(" step=\"");
198: printXmlText(os, _step);
199: os.print("\"");
200: }
201:
202: os.print(">");
203:
204: printXmlChildren(os);
205:
206: os.print("</c:forEach>");
207: }
208:
209: /**
210: * Generates the prologue for the c:forEach tag.
211: */
212: public void generatePrologue(JspJavaWriter out) throws Exception {
213: TagInstance parent = getParent().getTag();
214:
215: _tag = parent.findTag(getQName(), _attributeNames, false);
216:
217: if (_tag != null) {
218: _tagVar = _tag.getId();
219: } else {
220: String id = "_jsp_loop_" + _gen.uniqueId();
221:
222: _tag = parent.addTag(getQName(), null, null,
223: _attributeNames, _attributeValues, false);
224:
225: _tag.setId(id);
226:
227: _tagVar = _tag.getId();
228:
229: if (isInteger())
230: out.println("com.caucho.jsp.IntegerLoopSupportTag "
231: + _tagVar + " = null;");
232: else
233: out.println("com.caucho.jsp.IteratorLoopSupportTag "
234: + _tagVar + " = null;");
235: }
236:
237: generatePrologueChildren(out);
238: }
239:
240: private boolean hasDeclaration() {
241: return (_varStatus != null || hasTag());
242: }
243:
244: /**
245: * Returns the depth of the loop tags.
246: */
247: private int getDepth() {
248: JspNode node = this ;
249: int depth = 0;
250:
251: for (; !(node instanceof JspSegmentNode); node = node
252: .getParent()) {
253: if (node instanceof JstlCoreForEach) {
254: JstlCoreForEach forEach = (JstlCoreForEach) node;
255:
256: if (forEach.isInteger() == isInteger())
257: depth++;
258: }
259: }
260:
261: return depth;
262: }
263:
264: /**
265: * Returns true if this is the first declaration for the forEach
266: */
267: private boolean isFirst() {
268: JspNode node = this ;
269:
270: for (; !(node instanceof JspSegmentNode); node = node
271: .getParent()) {
272: }
273:
274: return isFirst(node, getDepth()) == 1;
275: }
276:
277: /**
278: * Returns true if this is the first declaration for the forEach
279: */
280: private int isFirst(JspNode node, int depth) {
281: if (node == this )
282: return 1;
283: else if (node instanceof JstlCoreForEach) {
284: JstlCoreForEach forEach = (JstlCoreForEach) node;
285:
286: if (forEach.isInteger() == isInteger()
287: && forEach.getDepth() == depth
288: && forEach.hasDeclaration())
289: return 0;
290: }
291:
292: if (node instanceof JspContainerNode) {
293: ArrayList<JspNode> children = ((JspContainerNode) node)
294: .getChildren();
295:
296: if (children == null)
297: return -1;
298:
299: for (int i = 0; i < children.size(); i++) {
300: JspNode child = children.get(i);
301:
302: int result = isFirst(child, depth);
303:
304: if (result >= 0)
305: return result;
306: }
307: }
308:
309: return -1;
310: }
311:
312: /**
313: * Generates the code for the c:forEach tag.
314: */
315: @Override
316: public void generate(JspJavaWriter out) throws Exception {
317: if (_items == null && _itemsAttr == null)
318: generateIntegerForEach(out);
319: else
320: generateCollectionForEach(out);
321: }
322:
323: /**
324: * Generates the code for the c:forEach tag.
325: */
326: public void generateIntegerForEach(JspJavaWriter out)
327: throws Exception {
328: if (_begin == null && _beginAttr == null)
329: throw error(L.l(
330: "required attribute 'begin' missing from <{0}>",
331: getTagName()));
332:
333: if (_end == null && _endAttr == null)
334: throw error(L.l(
335: "required attribute 'end' missing from <{0}>",
336: getTagName()));
337:
338: int uniqueId = _gen.uniqueId();
339:
340: String oldVar = "_jsp_oldVar_" + uniqueId;
341: String oldStatusVar = "_jsp_status_" + uniqueId;
342:
343: if (_tagVar != null) {
344: out.println("if (" + _tagVar + " == null)");
345: out.println(" " + _tagVar
346: + " = new com.caucho.jsp.IntegerLoopSupportTag();");
347:
348: if (hasTag()) {
349: JspNode parentTagNode = getParent().getParentTagNode();
350:
351: if (parentTagNode == null) {
352: out
353: .println(_tagVar
354: + ".setParent((javax.servlet.jsp.tagext.Tag) null);");
355: } else {
356: out.println(_tagVar + ".setParent("
357: + parentTagNode.getCustomTagName() + ");");
358: }
359: }
360: }
361:
362: String beginVar = "_jsp_begin_" + uniqueId;
363: String endVar = "_jsp_end_" + uniqueId;
364: String iVar = "_jsp_i_" + uniqueId;
365:
366: out.print("int " + beginVar + " = ");
367: if (_beginAttr != null)
368: out.print(_beginAttr.generateValue(int.class));
369: else
370: out.print(generateValue(int.class, _begin));
371: out.println(";");
372:
373: out.print("int " + endVar + " = ");
374: if (_endAttr != null)
375: out.print(_endAttr.generateValue(int.class));
376: else
377: out.print(generateValue(int.class, _end));
378: out.println(";");
379:
380: String stepVar = null;
381: if (_step != null || _stepAttr != null) {
382: stepVar = "_jsp_step_" + uniqueId;
383: out.print("int " + stepVar + " = ");
384:
385: if (_stepAttr != null)
386: out.print(_stepAttr.generateValue(int.class));
387: else
388: out.print(generateValue(int.class, _step));
389:
390: out.println(";");
391: } else
392: stepVar = "1";
393:
394: if (_tagVar != null)
395: out.println(_tagVar + ".init(" + beginVar + ", " + endVar
396: + ", " + stepVar + ");");
397:
398: if (_varStatus != null) {
399: out.print("Object " + oldStatusVar
400: + " = pageContext.putAttribute(\"");
401: out.print(escapeJavaString(_varStatus));
402: out.println("\", " + _tagVar + ");");
403: }
404:
405: /*
406: if (_var != null) {
407: out.print("Object " + oldVar + " = pageContext.getAttribute(\"");
408: out.print(escapeJavaString(_var));
409: out.println("\");");
410: }
411: */
412:
413: out.print("for (int " + iVar + " = " + beginVar + "; ");
414: out.print(iVar + " <= " + endVar + "; ");
415: out.println(iVar + " += " + stepVar + ") {");
416: out.pushDepth();
417:
418: if (_var != null) {
419: out.print("pageContext.setAttribute(\""
420: + escapeJavaString(_var) + "\"");
421: out.println(", new Integer(" + iVar + "));");
422: }
423:
424: if (_tagVar != null) {
425: out.println(_tagVar + ".setCurrent(" + iVar + ");");
426: }
427:
428: generateChildren(out);
429:
430: out.popDepth();
431: out.println("}");
432:
433: if (_var != null) {
434: out.print("pageContext.removeAttribute(\"");
435: out.print(escapeJavaString(_var));
436: out.println("\");");
437:
438: /*
439: out.print("pageContext.pageSetOrRemove(\"");
440: out.print(escapeJavaString(_var));
441: out.println("\", " + oldVar + ");");
442: */
443: }
444:
445: if (_varStatus != null) {
446: out.print("pageContext.removeAttribute(\"");
447: out.print(escapeJavaString(_varStatus));
448: out.println("\");");
449:
450: /*
451: out.print("pageContext.pageSetOrRemove(\"");
452: out.print(escapeJavaString(_varStatus));
453: out.println("\", " + oldStatusVar + ");");
454: */
455: }
456: }
457:
458: /**
459: * Generates the code for the c:forEach tag.
460: */
461: public void generateCollectionForEach(JspJavaWriter out)
462: throws Exception {
463: int uniqueId = _gen.uniqueId();
464:
465: String oldVar = "_jsp_oldVar_" + uniqueId;
466: String oldStatusVar = "_jsp_status_" + uniqueId;
467:
468: if (_tagVar != null) {
469: out.println("if (" + _tagVar + " == null)");
470: out
471: .println(" "
472: + _tagVar
473: + " = new com.caucho.jsp.IteratorLoopSupportTag();");
474:
475: if (hasTag()) {
476: JspNode parentTagNode = getParent().getParentTagNode();
477:
478: if (parentTagNode == null) {
479: out
480: .println(_tagVar
481: + ".setParent((javax.servlet.jsp.tagext.Tag) null);");
482: } else {
483: out.println(_tagVar + ".setParent("
484: + parentTagNode.getCustomTagName() + ");");
485: }
486: }
487: }
488:
489: String itemsVar = "_jsp_items_" + uniqueId;
490:
491: out.print("java.lang.Object " + itemsVar + " = ");
492: if (_itemsAttr != null)
493: out.print(_itemsAttr.generateValue(Object.class));
494: else
495: out.print(generateValue(Object.class, _items));
496: out.println(";");
497:
498: String mapperVar = "_jsp_vm_" + uniqueId;
499: String deferredValue = null;
500:
501: if (_items != null && _items.contains("#{")) {
502: deferredValue = "_caucho_value_expr_"
503: + _gen.addValueExpr(_items, "");
504: }
505:
506: if (deferredValue != null && _var != null) {
507: out
508: .print("javax.el.ValueExpression "
509: + mapperVar
510: + " = _jsp_env.getVariableMapper().resolveVariable(\"");
511: out.print(escapeJavaString(_var));
512: out.println("\");");
513: }
514:
515: String iterVar = "_jsp_iter_" + uniqueId;
516: String iVar = "_jsp_i_" + uniqueId;
517: out.println("java.util.Iterator " + iterVar
518: + " = com.caucho.jstl.rt.CoreForEachTag.getIterator("
519: + itemsVar + ");");
520:
521: String beginVar = null;
522: if (_beginAttr != null || _begin != null) {
523: beginVar = "_jsp_begin_" + uniqueId;
524: out.print("int " + beginVar + " = ");
525: if (_beginAttr != null)
526: out.print(_beginAttr.generateValue(int.class));
527: else
528: out.print(generateValue(int.class, _begin));
529: out.println(";");
530: }
531:
532: String intVar = "_jsp_int_" + uniqueId;
533: if (beginVar != null) {
534: out.print("for (int " + intVar + " = " + beginVar + ";");
535: out.println(intVar + " > 0; " + intVar + "--)");
536: out.println(" if (" + iterVar + ".hasNext()) " + iterVar
537: + ".next();");
538: }
539:
540: String endVar = null;
541: if (_endAttr != null || _end != null) {
542: endVar = "_jsp_end_" + uniqueId;
543:
544: out.print("int " + endVar + " = ");
545: if (_endAttr != null)
546: out.print(_endAttr.generateValue(int.class));
547: else
548: out.print(generateValue(int.class, _end));
549: out.println(";");
550: }
551:
552: String stepVar = null;
553: if (_step != null || _stepAttr != null) {
554: stepVar = "_jsp_step_" + uniqueId;
555: out.print("int " + stepVar + " = ");
556:
557: if (_stepAttr != null)
558: out.print(_stepAttr.generateValue(int.class));
559: else
560: out.print(generateValue(int.class, _step));
561:
562: out.println(";");
563: } else
564: stepVar = "1";
565:
566: if (_tagVar != null) {
567: out.print(_tagVar + ".init(");
568: if (beginVar != null)
569: out.print(beginVar + ", ");
570: else
571: out.print("0, ");
572:
573: if (endVar != null)
574: out.print(endVar + ", ");
575: else
576: out.print("Integer.MAX_VALUE, ");
577:
578: out.println(stepVar + ");");
579: }
580:
581: if (_varStatus != null) {
582: out.print("Object " + oldStatusVar
583: + " = pageContext.putAttribute(\"");
584: out.print(escapeJavaString(_varStatus));
585: out.println("\", " + _tagVar + ");");
586: }
587:
588: /*
589: if (_var != null) {
590: out.print("Object " + oldVar + " = pageContext.getAttribute(\"");
591: out.print(escapeJavaString(_var));
592: out.println("\");");
593: }
594: */
595:
596: if (endVar != null) {
597: String begin = beginVar == null ? "0" : beginVar;
598:
599: out.print("for (int " + intVar + " = " + begin + "; ");
600: out.print(intVar + " <= " + endVar);
601:
602: out.print(" && " + iterVar + ".hasNext(); ");
603:
604: out.println(intVar + " += " + stepVar + ") {");
605: } else
606: out.println("while (" + iterVar + ".hasNext()) {");
607:
608: out.pushDepth();
609:
610: out.println("Object " + iVar + " = " + iterVar + ".next();");
611:
612: if (_var != null) {
613: out.print("pageContext.setAttribute(\""
614: + escapeJavaString(_var) + "\"");
615: out.println(", " + iVar + ");");
616: }
617:
618: if (_tagVar != null) {
619: out.println(_tagVar + ".setCurrent(" + iVar + ", "
620: + iterVar + ".hasNext());");
621: }
622:
623: if (deferredValue != null && _var != null) {
624: out.print("_jsp_env.getVariableMapper().setVariable(\"");
625: out.print(escapeJavaString(_var));
626: out.print("\", ");
627: out.print("com.caucho.jstl.rt.CoreForEachTag.getExpr(");
628: out.print(deferredValue + ", " + _tagVar + ".getIndex(), "
629: + itemsVar);
630: out.println("));");
631: }
632:
633: generateChildren(out);
634:
635: if (!stepVar.equals("1")) {
636: String stepI = "_jsp_si_" + uniqueId;
637:
638: out.print("for (int " + stepI + " = " + stepVar + "; ");
639: out.println(stepI + " > 1; " + stepI + "--)");
640: out.println(" if (" + iterVar + ".hasNext()) " + iterVar
641: + ".next();");
642: out.println("if (! " + iterVar + ".hasNext())");
643: out.println(" break;");
644: }
645:
646: out.popDepth();
647: out.println("}");
648:
649: if (_var != null) {
650: // jsp/1cmg
651: out.print("pageContext.removeAttribute(\"");
652: out.print(escapeJavaString(_var));
653: out.println("\");");
654:
655: // restore EL variable
656: if (deferredValue != null && _var != null) {
657: out
658: .print("_jsp_env.getVariableMapper().setVariable(\"");
659: out.print(escapeJavaString(_var));
660: out.println("\", " + mapperVar + ");");
661: }
662: }
663:
664: if (_varStatus != null) {
665: // jsp/1cme
666: out.print("pageContext.removeAttribute(\"");
667: out.print(escapeJavaString(_varStatus));
668: out.println("\");");
669: }
670: }
671: }
|