001: /*******************************************************************************
002: * Copyright (c) 2000, 2005 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdi.internal;
011:
012: import java.io.ByteArrayOutputStream;
013: import java.io.DataInputStream;
014: import java.io.DataOutputStream;
015: import java.io.IOException;
016: import java.util.ArrayList;
017: import java.util.Iterator;
018: import java.util.List;
019:
020: import org.eclipse.jdi.internal.jdwp.JdwpCommandPacket;
021: import org.eclipse.jdi.internal.jdwp.JdwpID;
022: import org.eclipse.jdi.internal.jdwp.JdwpObjectID;
023: import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket;
024:
025: import com.sun.jdi.ArrayReference;
026: import com.sun.jdi.ClassNotLoadedException;
027: import com.sun.jdi.InternalException;
028: import com.sun.jdi.InvalidTypeException;
029: import com.sun.jdi.ObjectCollectedException;
030: import com.sun.jdi.Type;
031: import com.sun.jdi.Value;
032:
033: /**
034: * this class implements the corresponding interfaces
035: * declared by the JDI specification. See the com.sun.jdi package
036: * for more information.
037: *
038: */
039: public class ArrayReferenceImpl extends ObjectReferenceImpl implements
040: ArrayReference {
041: /** JDWP Tag. */
042: public static final byte tag = JdwpID.ARRAY_TAG;
043:
044: private int fLength = -1;
045:
046: /**
047: * Creates new ArrayReferenceImpl.
048: */
049: public ArrayReferenceImpl(VirtualMachineImpl vmImpl,
050: JdwpObjectID objectID) {
051: super ("ArrayReference", vmImpl, objectID); //$NON-NLS-1$
052: }
053:
054: /**
055: * @returns tag.
056: */
057: public byte getTag() {
058: return tag;
059: }
060:
061: /**
062: * @returns Returns an array component value.
063: */
064: public Value getValue(int index) throws IndexOutOfBoundsException {
065: return (Value) getValues(index, 1).get(0);
066: }
067:
068: /**
069: * @returns Returns all of the components in this array.
070: */
071: public List getValues() {
072: return getValues(0, -1);
073: }
074:
075: /**
076: * @returns Returns a range of array components.
077: */
078: public List getValues(int firstIndex, int length)
079: throws IndexOutOfBoundsException {
080:
081: int arrayLength = length();
082:
083: if (firstIndex < 0 || firstIndex >= arrayLength) {
084: throw new IndexOutOfBoundsException(
085: JDIMessages.ArrayReferenceImpl_Invalid_index_1);
086: }
087:
088: if (length == -1) {
089: // length == -1 means all elements to the end.
090: length = arrayLength - firstIndex;
091: } else if (length < -1) {
092: throw new IndexOutOfBoundsException(
093: JDIMessages.ArrayReferenceImpl_Invalid_number_of_value_to_get_from_array_1);
094: } else if (firstIndex + length > arrayLength) {
095: throw new IndexOutOfBoundsException(
096: JDIMessages.ArrayReferenceImpl_Attempted_to_get_more_values_from_array_than_length_of_array_2);
097: }
098:
099: // Note that this information should not be cached.
100: initJdwpRequest();
101: try {
102: ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
103: DataOutputStream outData = new DataOutputStream(outBytes);
104: write(this , outData); // arrayObject
105: writeInt(firstIndex, "firstIndex", outData); //$NON-NLS-1$
106: writeInt(length, "length", outData); //$NON-NLS-1$
107:
108: JdwpReplyPacket replyPacket = requestVM(
109: JdwpCommandPacket.AR_GET_VALUES, outBytes);
110: switch (replyPacket.errorCode()) {
111: case JdwpReplyPacket.INVALID_INDEX:
112: throw new IndexOutOfBoundsException(
113: JDIMessages.ArrayReferenceImpl_Invalid_index_of_array_reference_given_1);
114: }
115: defaultReplyErrorHandler(replyPacket.errorCode());
116:
117: DataInputStream replyData = replyPacket.dataInStream();
118:
119: /* NOTE: The JDWP documentation is not clear on this: it turns out that the following is received from the VM:
120: * - type tag;
121: * - length of array;
122: * - values of elements.
123: */
124:
125: int type = readByte("type", JdwpID.tagMap(), replyData); //$NON-NLS-1$
126: int readLength = readInt("length", replyData); //$NON-NLS-1$
127: // See also ValueImpl.
128: switch (type) {
129: // Multidimensional array.
130: case ArrayReferenceImpl.tag:
131: // Object references.
132: case ClassLoaderReferenceImpl.tag:
133: case ClassObjectReferenceImpl.tag:
134: case StringReferenceImpl.tag:
135: case ObjectReferenceImpl.tag:
136: case ThreadGroupReferenceImpl.tag:
137: case ThreadReferenceImpl.tag:
138: return readObjectSequence(readLength, replyData);
139:
140: // Primitive type.
141: case BooleanValueImpl.tag:
142: case ByteValueImpl.tag:
143: case CharValueImpl.tag:
144: case DoubleValueImpl.tag:
145: case FloatValueImpl.tag:
146: case IntegerValueImpl.tag:
147: case LongValueImpl.tag:
148: case ShortValueImpl.tag:
149: return readPrimitiveSequence(readLength, type,
150: replyData);
151:
152: case VoidValueImpl.tag:
153: case 0:
154: default:
155: throw new InternalException(
156: JDIMessages.ArrayReferenceImpl_Invalid_ArrayReference_Value_tag_encountered___2
157: + type);
158: }
159: } catch (IOException e) {
160: defaultIOExceptionHandler(e);
161: return null;
162: } finally {
163: handledJdwpRequest();
164: }
165: }
166:
167: /**
168: * @returns Returns sequence of object reference values.
169: */
170: private List readObjectSequence(int length, DataInputStream in)
171: throws IOException {
172: List elements = new ArrayList(length);
173: for (int i = 0; i < length; i++) {
174: ValueImpl value = ObjectReferenceImpl.readObjectRefWithTag(
175: this , in);
176: elements.add(value);
177: }
178: return elements;
179: }
180:
181: /**
182: * @returns Returns sequence of values of primitive type.
183: */
184: private List readPrimitiveSequence(int length, int type,
185: DataInputStream in) throws IOException {
186: List elements = new ArrayList(length);
187: for (int i = 0; i < length; i++) {
188: ValueImpl value = ValueImpl.readWithoutTag(this , type, in);
189: elements.add(value);
190: }
191: return elements;
192: }
193:
194: /**
195: * @returns Returns the number of components in this array.
196: */
197: public int length() {
198: if (fLength == -1) {
199: initJdwpRequest();
200: try {
201: JdwpReplyPacket replyPacket = requestVM(
202: JdwpCommandPacket.AR_LENGTH, this );
203: defaultReplyErrorHandler(replyPacket.errorCode());
204: DataInputStream replyData = replyPacket.dataInStream();
205: fLength = readInt("length", replyData); //$NON-NLS-1$
206: } catch (IOException e) {
207: defaultIOExceptionHandler(e);
208: return 0;
209: } finally {
210: handledJdwpRequest();
211: }
212: }
213: return fLength;
214: }
215:
216: /**
217: * Replaces an array component with another value.
218: */
219: public void setValue(int index, Value value)
220: throws InvalidTypeException, ClassNotLoadedException {
221: ArrayList list = new ArrayList(1);
222: list.add(value);
223: setValues(index, list, 0, 1);
224: }
225:
226: /**
227: * Replaces all array components with other values.
228: */
229: public void setValues(List values) throws InvalidTypeException,
230: ClassNotLoadedException {
231: setValues(0, values, 0, -1);
232: }
233:
234: /**
235: * Replaces a range of array components with other values.
236: */
237: public void setValues(int index, List values, int srcIndex,
238: int length) throws InvalidTypeException,
239: ClassNotLoadedException {
240:
241: int valuesSize = values.size();
242: int arrayLength = length();
243:
244: if (index < 0 || index >= arrayLength) {
245: throw new IndexOutOfBoundsException(
246: JDIMessages.ArrayReferenceImpl_Invalid_index_1);
247: }
248: if (srcIndex < 0 || srcIndex >= valuesSize) {
249: throw new IndexOutOfBoundsException(
250: JDIMessages.ArrayReferenceImpl_Invalid_srcIndex_2);
251: }
252:
253: if (length < -1) {
254: throw new IndexOutOfBoundsException(
255: JDIMessages.ArrayReferenceImpl_Invalid_number_of_value_to_set_in_array_3);
256: } else if (length == -1) {
257: // length == -1 indicates as much values as possible.
258: length = arrayLength - index;
259: int lengthTmp = valuesSize - srcIndex;
260: if (lengthTmp < length) {
261: length = lengthTmp;
262: }
263: } else if (index + length > arrayLength) {
264: throw new IndexOutOfBoundsException(
265: JDIMessages.ArrayReferenceImpl_Attempted_to_set_more_values_in_array_than_length_of_array_3);
266: } else if (srcIndex + length > valuesSize) {
267: // Check if enough values are given.
268: throw new IndexOutOfBoundsException(
269: JDIMessages.ArrayReferenceImpl_Attempted_to_set_more_values_in_array_than_given_4);
270: }
271:
272: // check and convert the values if needed.
273: List checkedValues = checkValues(values.subList(srcIndex,
274: srcIndex + length), ((ArrayTypeImpl) referenceType())
275: .componentType());
276:
277: // Note that this information should not be cached.
278: initJdwpRequest();
279: try {
280: ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
281: DataOutputStream outData = new DataOutputStream(outBytes);
282: write(this , outData);
283: writeInt(index, "index", outData); //$NON-NLS-1$
284: writeInt(length, "length", outData); //$NON-NLS-1$
285: Iterator iterValues = checkedValues.iterator();
286: while (iterValues.hasNext()) {
287: ValueImpl value = (ValueImpl) iterValues.next();
288: if (value != null) {
289: value.write(this , outData);
290: } else {
291: ValueImpl.writeNull(this , outData);
292: }
293: }
294:
295: JdwpReplyPacket replyPacket = requestVM(
296: JdwpCommandPacket.AR_SET_VALUES, outBytes);
297: switch (replyPacket.errorCode()) {
298: case JdwpReplyPacket.TYPE_MISMATCH:
299: throw new InvalidTypeException();
300: case JdwpReplyPacket.INVALID_CLASS:
301: throw new ClassNotLoadedException(type().name());
302: }
303: defaultReplyErrorHandler(replyPacket.errorCode());
304: } catch (IOException e) {
305: defaultIOExceptionHandler(e);
306: } finally {
307: handledJdwpRequest();
308: }
309: }
310:
311: /**
312: * Check the type and the vm of the values. If the given type is a primitive type,
313: * the values may be convert for match this type.
314: * @see ValueImpl#checkValue(Value, Type, VirtualMachineImpl)
315: */
316: private List checkValues(List values, Type type)
317: throws InvalidTypeException {
318: List checkedValues = new ArrayList(values.size());
319: Iterator iterValues = values.iterator();
320: while (iterValues.hasNext()) {
321: checkedValues.add(ValueImpl.checkValue((Value) iterValues
322: .next(), type, virtualMachineImpl()));
323: }
324: return checkedValues;
325: }
326:
327: /**
328: * @return Returns description of Mirror object.
329: */
330: public String toString() {
331: try {
332: StringBuffer buf = new StringBuffer(type().name());
333: // Insert length of string between (last) square braces.
334: buf.insert(buf.length() - 1, length());
335: // Append space and idString.
336: buf.append(' ');
337: buf.append(idString());
338: return buf.toString();
339: } catch (ObjectCollectedException e) {
340: return JDIMessages.ArrayReferenceImpl__Garbage_Collected__ArrayReference_5
341: + "[" + length() + "] " + idString(); //$NON-NLS-1$ //$NON-NLS-2$
342: } catch (Exception e) {
343: return fDescription;
344: }
345: }
346:
347: /**
348: * @return Reads JDWP representation and returns new instance.
349: */
350: public static ArrayReferenceImpl read(MirrorImpl target,
351: DataInputStream in) throws IOException {
352: VirtualMachineImpl vmImpl = target.virtualMachineImpl();
353: JdwpObjectID ID = new JdwpObjectID(vmImpl);
354: ID.read(in);
355: if (target.fVerboseWriter != null) {
356: target.fVerboseWriter.println("arrayReference", ID.value()); //$NON-NLS-1$
357: }
358:
359: if (ID.isNull()) {
360: return null;
361: }
362:
363: ArrayReferenceImpl mirror = new ArrayReferenceImpl(vmImpl, ID);
364: return mirror;
365: }
366: }
|