001: /**
002: * MVEL (The MVFLEX Expression Language)
003: *
004: * Copyright (C) 2007 Christopher Brock, MVFLEX/Valhalla Project and the Codehaus
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: */package org.mvel.util;
019:
020: import static org.mvel.util.ParseTools.balancedCapture;
021:
022: import static java.lang.Character.isWhitespace;
023: import static java.lang.System.arraycopy;
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.List;
027: import java.util.Map;
028:
029: /**
030: * This is the inline collection sub-parser. It produces a skeleton model of the collection which is in turn translated
031: * into a sequenced AST to produce the collection efficiently at runtime, and passed off to one of the JIT's if
032: * configured.
033: *
034: * @author Christopher Brock
035: */
036: public class CollectionParser {
037: private char[] property;
038:
039: private int cursor;
040: private int length;
041: private int start;
042:
043: private int type;
044:
045: public static final int LIST = 0;
046: public static final int ARRAY = 1;
047: public static final int MAP = 2;
048:
049: private static final Object[] EMPTY_ARRAY = new Object[0];
050:
051: public CollectionParser() {
052: }
053:
054: public CollectionParser(int type) {
055: this .type = type;
056: }
057:
058: public Object parseCollection(char[] property) {
059: this .cursor = 0;
060: if ((this .length = (this .property = property).length) > 0)
061: while (length > 0 && isWhitespace(property[length - 1]))
062: length--;
063:
064: return parseCollection();
065: }
066:
067: private Object parseCollection() {
068: if (length == 0) {
069: if (type == LIST)
070: return new ArrayList();
071: else
072: return EMPTY_ARRAY;
073: }
074:
075: Map<Object, Object> map = null;
076: List<Object> list = null;
077:
078: if (type != -1) {
079: switch (type) {
080: case ARRAY:
081: case LIST:
082: list = new ArrayList<Object>();
083: break;
084: case MAP:
085: map = new HashMap<Object, Object>();
086: break;
087: }
088: }
089:
090: Object curr = null;
091: int newType = -1;
092:
093: for (; cursor < length; cursor++) {
094: switch (property[cursor]) {
095: case '{':
096: if (newType == -1) {
097: newType = ARRAY;
098: }
099:
100: case '[':
101: if (newType == -1) {
102: newType = LIST;
103: }
104:
105: /**
106: * Sub-parse nested collections.
107: */
108: Object o = new CollectionParser(newType)
109: .parseCollection(subset(property,
110: (start = cursor) + 1,
111: cursor = balancedCapture(property,
112: start, property[start])));
113:
114: if (type == MAP) {
115: map.put(curr, o);
116: } else {
117: list.add(curr = o);
118: }
119:
120: if ((start = ++cursor) < (length - 1)
121: && property[cursor] == ',') {
122: start = cursor + 1;
123: }
124:
125: continue;
126:
127: case '(':
128: cursor = balancedCapture(property, cursor,
129: property[cursor]);
130:
131: break;
132:
133: case '\"':
134: case '\'':
135: cursor = balancedCapture(property, cursor,
136: property[cursor]);
137:
138: break;
139:
140: case ',':
141: if (type != MAP) {
142: list
143: .add(new String(property, start, cursor
144: - start));
145: } else {
146: map.put(curr, new String(property, start, cursor
147: - start).trim());
148: }
149:
150: start = cursor + 1;
151:
152: break;
153:
154: case ':':
155: if (type != MAP) {
156: map = new HashMap<Object, Object>();
157: type = MAP;
158: }
159: curr = new String(property, start, cursor - start)
160: .trim();
161:
162: start = cursor + 1;
163: break;
164: }
165: }
166:
167: if (start < length) {
168: if (cursor < (length - 1))
169: cursor++;
170:
171: if (type == MAP) {
172: map.put(curr, new String(property, start, cursor
173: - start).trim());
174: } else {
175: if (cursor < length)
176: cursor++;
177: list.add(new String(property, start, cursor - start)
178: .trim());
179: }
180: }
181:
182: switch (type) {
183: case MAP:
184: return map;
185: case ARRAY:
186: return list.toArray();
187: default:
188: return list;
189: }
190: }
191:
192: private static char[] subset(char[] property, int start, int end) {
193: while (start < (end - 1) && isWhitespace(property[start]))
194: start++;
195:
196: char[] newA = new char[end - start];
197: //arraycopy(property, start, newA, 0, end - start);
198: for (int i = 0; i < newA.length; i++) {
199: newA[i] = property[i + start];
200: }
201:
202: return newA;
203: }
204:
205: public int getCursor() {
206: return cursor;
207: }
208:
209: }
|