001: /*
002: * Created on Apr 17, 2005
003: *
004: */
005: package com.ibatis.sqlmap.engine.mapping.sql.dynamic.elements;
006:
007: import java.lang.reflect.Array;
008: import java.util.ArrayList;
009: import java.util.Arrays;
010: import java.util.Collection;
011: import java.util.HashMap;
012: import java.util.Iterator;
013: import java.util.List;
014: import java.util.Map;
015:
016: import com.ibatis.sqlmap.client.SqlMapException;
017:
018: /**
019: * @author Brandon Goodin
020: */
021: public class IterateContext implements Iterator {
022:
023: private static final String PROCESS_INDEX = "ProcessIndex";
024: private static final String PROCESS_STRING = "ProcessString";
025:
026: private Iterator iterator;
027: private int index = -1;
028:
029: private String property;
030: private boolean allowNext = true;
031:
032: private boolean isFinal = false;
033: private SqlTag tag;
034:
035: private IterateContext parent;
036:
037: /**
038: * This variable is true if some of the sub elements have
039: * actually produced content. This is used to test
040: * whether to add the open and conjunction text to the
041: * generated statement.
042: *
043: * This variable is used to replace the deprecated and dangerous
044: * isFirst method.
045: */
046: private boolean someSubElementsHaveContent;
047:
048: /**
049: * This variable is set by the doEndFragment method in IterateTagHandler
050: * to specify that the first content producing sub element has happened.
051: * The doPrepend method will test the value to know whether or not
052: * to process the prepend.
053: *
054: * This variable is used to replace the deprecated and dangerous
055: * isFirst method.
056: */
057: private boolean isPrependEnabled;
058:
059: public IterateContext(Object collection, SqlTag tag,
060: IterateContext parent) {
061: this .parent = parent;
062: this .tag = tag;
063: if (collection instanceof Collection) {
064: this .iterator = ((Collection) collection).iterator();
065: } else if (collection instanceof Iterator) {
066: this .iterator = ((Iterator) collection);
067: } else if (collection.getClass().isArray()) {
068: List list = arrayToList(collection);
069: this .iterator = list.iterator();
070: } else {
071: throw new SqlMapException(
072: "ParameterObject or property was not a Collection, Array or Iterator.");
073: }
074: }
075:
076: public boolean hasNext() {
077: return iterator != null && iterator.hasNext();
078: }
079:
080: public Object next() {
081: index++;
082: return iterator.next();
083: }
084:
085: public void remove() {
086: iterator.remove();
087: }
088:
089: public int getIndex() {
090: return index;
091: }
092:
093: /**
094: *
095: * @return
096: * @deprecated This method should not be used to decide whether or not to
097: * add prepend and open text to the generated statement. Rather, use the
098: * methods isPrependEnabled() and someSubElementsHaveContent().
099: */
100: public boolean isFirst() {
101: return index == 0;
102: }
103:
104: public boolean isLast() {
105: return iterator != null && !iterator.hasNext();
106: }
107:
108: private List arrayToList(Object array) {
109: List list = null;
110: if (array instanceof Object[]) {
111: list = Arrays.asList((Object[]) array);
112: } else {
113: list = new ArrayList();
114: for (int i = 0, n = Array.getLength(array); i < n; i++) {
115: list.add(Array.get(array, i));
116: }
117: }
118: return list;
119: }
120:
121: /**
122: * @return Returns the property.
123: */
124: public String getProperty() {
125: return property;
126: }
127:
128: /**
129: * This property specifies whether to increment the iterate in
130: * the doEndFragment. The ConditionalTagHandler has the ability
131: * to increment the IterateContext, so it is neccessary to avoid
132: * incrementing in both the ConditionalTag and the IterateTag.
133: *
134: * @param property The property to set.
135: */
136: public void setProperty(String property) {
137: this .property = property;
138: }
139:
140: /**
141: * @return Returns the allowNext.
142: */
143: public boolean isAllowNext() {
144: return allowNext;
145: }
146:
147: /**
148: * @param performIterate The allowNext to set.
149: */
150: public void setAllowNext(boolean performIterate) {
151: this .allowNext = performIterate;
152: }
153:
154: /**
155: * @return Returns the tag.
156: */
157: public SqlTag getTag() {
158: return tag;
159: }
160:
161: /**
162: * @param tag The tag to set.
163: */
164: public void setTag(SqlTag tag) {
165: this .tag = tag;
166: }
167:
168: /**
169: *
170: * @return
171: */
172: public boolean isFinal() {
173: return isFinal;
174: }
175:
176: /**
177: * This attribute is used to mark whether an iterate tag is
178: * in it's final iteration. Since the ConditionalTagHandler
179: * can increment the iterate the final iterate in the doEndFragment
180: * of the IterateTagHandler needs to know it is in it's final iterate.
181: *
182: * @param aFinal
183: */
184: public void setFinal(boolean aFinal) {
185: isFinal = aFinal;
186: }
187:
188: /**
189: * Returns the last property of any bean specified in this IterateContext.
190: * @return The last property of any bean specified in this IterateContext.
191: */
192: public String getEndProperty() {
193: if (parent != null) {
194: int parentPropertyIndex = property.indexOf(parent
195: .getProperty());
196: if (parentPropertyIndex > -1) {
197: int endPropertyIndex1 = property.indexOf(']',
198: parentPropertyIndex);
199: int endPropertyIndex2 = property.indexOf('.',
200: parentPropertyIndex);
201: return property.substring(parentPropertyIndex
202: + Math
203: .max(endPropertyIndex1,
204: endPropertyIndex2) + 1,
205: property.length());
206: } else {
207: return property;
208: }
209: } else {
210: return property;
211: }
212: }
213:
214: /**
215: * Replaces value of a tag property to match it's value with current iteration and all other iterations.
216: * @param tagProperty the property of a TagHandler.
217: * @return A Map containing the modified tag property in PROCESS_STRING key and the index where the modification occured in PROCESS_INDEX key.
218: */
219: protected Map processTagProperty(String tagProperty) {
220: if (parent != null) {
221: Map parentResult = parent.processTagProperty(tagProperty);
222: return this .addIndex((String) parentResult
223: .get(PROCESS_STRING), ((Integer) parentResult
224: .get(PROCESS_INDEX)).intValue());
225: } else {
226: return this .addIndex(tagProperty, 0);
227: }
228: }
229:
230: /**
231: * Replaces value of a tag property to match it's value with current iteration and all other iterations.
232: * @param tagProperty the property of a TagHandler.
233: * @return The tag property with all "[]" replaced with the correct iteration value.
234: */
235: public String addIndexToTagProperty(String tagProperty) {
236: Map map = this .processTagProperty(tagProperty);
237: return (String) map.get(PROCESS_STRING);
238: }
239:
240: /**
241: * Adds index value to the first found property matching this Iteration starting at index startIndex.
242: * @param input The input String.
243: * @param startIndex The index where search for property begins.
244: * @return A Map containing the modified tag property in PROCESS_STRING key and the index where the modification occured in PROCESS_INDEX key.
245: */
246: protected Map addIndex(String input, int startIndex) {
247: String endProperty = getEndProperty() + "[";
248: int propertyIndex = input.indexOf(endProperty, startIndex);
249: int modificationIndex = 0;
250: // Is the iterate property in the tag property at all?
251: if (propertyIndex > -1) {
252: // Make sure the tag property does not already have a number.
253: if (input.charAt(propertyIndex + endProperty.length()) == ']') {
254: // Add iteration number to property.
255: input = input.substring(0, propertyIndex
256: + endProperty.length())
257: + this .getIndex()
258: + input.substring(propertyIndex
259: + endProperty.length());
260: modificationIndex = propertyIndex
261: + endProperty.length();
262: }
263: }
264: Map ret = new HashMap();
265: ret.put(PROCESS_INDEX, new Integer(modificationIndex));
266: ret.put(PROCESS_STRING, input);
267: return ret;
268: }
269:
270: public IterateContext getParent() {
271: return parent;
272: }
273:
274: public void setParent(IterateContext parent) {
275: this .parent = parent;
276: }
277:
278: public boolean someSubElementsHaveContent() {
279: return someSubElementsHaveContent;
280: }
281:
282: public void setSomeSubElementsHaveContent(
283: boolean someSubElementsHaveContent) {
284: this .someSubElementsHaveContent = someSubElementsHaveContent;
285: }
286:
287: public boolean isPrependEnabled() {
288: return isPrependEnabled;
289: }
290:
291: public void setPrependEnabled(boolean isPrependEnabled) {
292: this.isPrependEnabled = isPrependEnabled;
293: }
294: }
|