001: /*
002: * Copyright 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.lang;
017:
018: /**
019: * This is a magic class the compiler uses as a base class for injected array
020: * classes.
021: */
022: public final class Array {
023:
024: /*
025: * TODO: static init instead of lazy init when we can elide the clinit calls.
026: */
027:
028: static final int FALSE_SEED_TYPE = 2;
029:
030: static final int NULL_SEED_TYPE = 0;
031:
032: static final int ZERO_SEED_TYPE = 1;
033:
034: /**
035: * Stores the prototype Array so that arrays can get their polymorphic methods
036: * via expando.
037: */
038: private static Array protoTypeArray;
039:
040: /**
041: * Creates a copy of a subrange of the specified array.
042: */
043: public static <T> T[] cloneSubrange(T[] array, int fromIndex,
044: int toIndex) {
045: Array a = asArrayType(array);
046: Array result = arraySlice(a, fromIndex, toIndex);
047: initValues(a.getClass(), a.typeId, a.queryId, result);
048: return asArray(result);
049: }
050:
051: /**
052: * Creates a new array of the exact same type as a given array but with the
053: * specified length.
054: */
055: public static <T> T[] clonify(T[] array, int length) {
056: Array a = asArrayType(array);
057: Array result = createFromSeed(NULL_SEED_TYPE, length);
058: initValues(a.getClass(), a.typeId, a.queryId, result);
059: return asArray(result);
060: }
061:
062: /**
063: * Creates an array like "new T[a][b][c][][]" by passing in a native JSON
064: * array, [a, b, c].
065: *
066: * @param arrayClass the class of the array
067: * @param typeId the typeId of the array
068: * @param queryId the queryId of the array
069: * @param length the length of the array
070: * @param seedType the primitive type of the array; 0: null; 1: zero; 2: false
071: * @return the new array
072: */
073: public static Array initDim(Class arrayClass, int typeId,
074: int queryId, int length, int seedType) {
075: Array result = createFromSeed(seedType, length);
076: initValues(arrayClass, typeId, queryId, result);
077: return result;
078: }
079:
080: /**
081: * Creates an array like "new T[a][b][c][][]" by passing in a native JSON
082: * array, [a, b, c].
083: *
084: * @param arrayClasses the class of each dimension of the array
085: * @param typeIdExprs the typeId at each dimension, from highest to lowest
086: * @param queryIdExprs the queryId at each dimension, from highest to lowest
087: * @param dimExprs the length at each dimension, from highest to lower
088: * @param seedType the primitive type of the array; 0: null; 1: zero; 2: false
089: * @return the new array
090: */
091: public static Array initDims(Class arrayClasses[],
092: int[] typeIdExprs, int[] queryIdExprs, int[] dimExprs,
093: int seedType) {
094: return initDims(arrayClasses, typeIdExprs, queryIdExprs,
095: dimExprs, 0, dimExprs.length, seedType);
096: }
097:
098: /**
099: * Creates an array like "new T[][]{a,b,c,d}" by passing in a native JSON
100: * array, [a, b, c, d].
101: *
102: * @param arrayClass the class of the array
103: * @param typeId the typeId of the array
104: * @param queryId the queryId of the array
105: * @param array the JSON array that will be transformed into a GWT array
106: * @return values; having wrapped it for GWT
107: */
108: public static final Array initValues(Class arrayClass, int typeId,
109: int queryId, Array array) {
110: if (protoTypeArray == null) {
111: protoTypeArray = new Array();
112: }
113: wrapArray(array, protoTypeArray);
114: array.arrayClass = arrayClass;
115: array.typeId = typeId;
116: array.queryId = queryId;
117: return array;
118: }
119:
120: /**
121: * Performs an array assignment, checking for valid index and type.
122: */
123: public static Object setCheck(Array array, int index, Object value) {
124: if (value != null && array.queryId != 0
125: && !Cast.instanceOf(value, array.queryId)) {
126: throw new ArrayStoreException();
127: }
128: return set(array, index, value);
129: }
130:
131: private static native Array arraySlice(Array array, int fromIndex,
132: int toIndex) /*-{
133: return array.slice(fromIndex, toIndex);
134: }-*/;
135:
136: /**
137: * Use JSNI to effect a castless type change.
138: */
139: private static native <T> T[] asArray(Array array) /*-{
140: return array;
141: }-*/;
142:
143: /**
144: * Use JSNI to effect a castless type change.
145: */
146: private static native <T> Array asArrayType(T[] array) /*-{
147: return array;
148: }-*/;
149:
150: /**
151: * Creates a primitive JSON array of a given seedType.
152: *
153: * @param seedType the primitive type of the array; 0: null; 1: zero; 2: false
154: * @param length the requested length
155: * @see #NULL_ARRAY
156: * @see #ZERO_ARRAY
157: * @see #FALSE_ARRAY
158: * @return the new JSON array
159: */
160: private static native Array createFromSeed(int seedType, int length) /*-{
161: var seedArray = [null, 0, false];
162: var value = seedArray[seedType];
163: var array = new Array(length);
164: for (var i = 0; i < length; ++i) {
165: array[i] = value;
166: }
167: return array;
168: }-*/;
169:
170: private static Array initDims(Class arrayClasses[],
171: int[] typeIdExprs, int[] queryIdExprs, int[] dimExprs,
172: int index, int count, int seedType) {
173: int length = dimExprs[index];
174: if (length < 0) {
175: throw new NegativeArraySizeException();
176: }
177:
178: boolean isLastDim = (index == (count - 1));
179:
180: Array result = createFromSeed(isLastDim ? seedType
181: : NULL_SEED_TYPE, length);
182: initValues(arrayClasses[index], typeIdExprs[index],
183: queryIdExprs[index], result);
184:
185: if (!isLastDim) {
186: // Recurse to next dimension.
187: ++index;
188: for (int i = 0; i < length; ++i) {
189: set(result, i, initDims(arrayClasses, typeIdExprs,
190: queryIdExprs, dimExprs, index, count, seedType));
191: }
192: }
193: return result;
194: }
195:
196: /**
197: * Sets a value in the array.
198: */
199: private static native Object set(Array array, int index,
200: Object value) /*-{
201: return array[index] = value;
202: }-*/;
203:
204: private static native Array wrapArray(Array array,
205: Array protoTypeArray) /*-{
206: for (var i in protoTypeArray) {
207: // Only copy non-null values over; this generally means only functions
208: // will get copied over, and not fields, which is fine because we will
209: // setup the fields manually and it's best if length doesn't get blown
210: // away.
211: var toCopy = protoTypeArray[i];
212: if (toCopy) {
213: array[i] = toCopy;
214: }
215: }
216: return array;
217: }-*/;
218:
219: /*
220: * Explicitly initialize all fields to JS false values; see comment in
221: * wrapArray(Array, Array).
222: */
223: public int length = 0;
224: protected Class arrayClass = null;
225: protected int queryId = 0;
226:
227: @Override
228: public Class getClass() {
229: return arrayClass;
230: }
231: }
|