Index: ArrayHelper.java =================================================================== RCS file: ArrayHelper.java diff -N ArrayHelper.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ArrayHelper.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,564 @@ +/* + * Copyright 2005 Clark Updike + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.python.core; + +import java.lang.reflect.Array; +import java.util.Arrays; + +/** + * Abstract class that manages bulk structural and data operations + * on arrays, defering type-specific element-wise operations to the + * subclass. Subclasses supply the underlying array and the + * type-specific operations--greatly reducing the need for casting + * (thus achieving array-like performances with collection-like + * flexibility). Also includes + * functionality to support integration with the the jdk's + * collections (via methods that return a modification increment).
+ * Subclasses will want to provide the following methods (which are + * not declared in this class since subclasses should specify the + * explicit return type): + *
<type> get(int)
void set(int, <type>)
void add(<type>)
void add(int, <type>)
<type>[] toArray()
+ * Clone cannot be supported since the array is not held locally. + * But the @link #ArrayHelper(ArrayHelper) constructor can be used + * for suclasses that need to support clone. + *
+ * This "type-specific collections" approach was originally developed
+ * by Dennis Sosnoski, who provides a more complete library at the
+ * referenced URL. Sosnoski's library does not integrate with the
+ * jdk collection classes but provides collection-like classes.
+ *
+ * @see
+ * Sosnoski's Type-Specific Collection Library
+ * @author Clark Updike
+ */
+public abstract class ArrayHelper {
+
+ /**
+ * Size of the current array, which can be larger than the
+ * size
field.
+ */
+ protected int capacity;
+
+ /**
+ * The number of values currently present in the array.
+ */
+ protected int size;
+
+ /**
+ * The modification count increment indicates if a structural change
+ * occured as a result of an operation that would make concurrent iteration
+ * over the array invalid. It is typically used by subclasses that
+ * extend AbstractList
, by adding the value to
+ * AbstractList.modCount
after performing a potentially
+ * structure-altering operation. A value of 0 indicates that
+ * it is still valid to iterate over the array. A value of 1
+ * indicates it is no longer valid to iterate over the range.
+ * This class uses a somewhat stricter semantic for modCount
.
+ * Namely, modCountIncr
is only set to 1 if a structural
+ * change occurred. The jdk collections generally increment
+ * modCount
if a potentially structure-altering method
+ * is called, regardless of whether or not a change actually occurred.
+ * @see java.util.AbstractList#modCount
+ */
+ protected int modCountIncr;
+
+ /**
+ * Since ArrayHelper can support a clone method, this facilitates
+ * sublcasses that want to implement clone (poor man's cloning).
+ * Sublclasses can then do this:
+ *
+ * public MyManagedArray(MyManagedArray toCopy) { + * super(this); + * this.baseArray = (+ * @param toCopy + */ + public ArrayHelper(ArrayHelper toCopy) { + capacity = toCopy.capacity; + // let modCountIncr default to 0 + size = toCopy.size; + } + + /** + * Use when the subclass has a preexisting array. + * @param size the initial size of the array + */ + public ArrayHelper(int size) { + this.size = size; + capacity = size; + } + + /** + * Creates the managed array with a default size of 10. + * + * @param type array element type (primitive type or object class) + */ + public ArrayHelper(Class type) { + this(type, 10); + } + + /** + * Construtor for multi-dimensional array types. + * For example,)toCopy.copyArray(); + * this.someProp = toCopy.someProp; + * + * } + * + * public Object clone() { + * return new MyManagedArray(this); + * } + *
char[][]
. This class only manages the
+ * top level dimension of the array. For single dimension
+ * arrays (the more typical usage), use the other constructors.
+ *
+ * @see Array#newInstance(java.lang.Class, int[])
+ * @param type Array element type (primitive type or object class).
+ * @param dimensions An int array specifying the dimensions. For
+ * a 2D array, something like new int[] {10,0}
to
+ * create 10 elements each of which can hold an reference to an
+ * array of the same type.
+ */
+ public ArrayHelper(Class type, int[] dimensions) {
+ Object array = Array.newInstance(type, dimensions);
+ capacity = dimensions[0];
+ setArray(array);
+ }
+
+ /**
+ * Creates the managed array with the specified size.
+ * @param type array element type (primitive type or object class)
+ * @param size number of elements initially allowed in array
+ */
+ public ArrayHelper(Class type, int size) {
+ Object array = Array.newInstance(type, size);
+ capacity = Math.max(size, 10);
+ setArray(array);
+ }
+
+ /**
+ * Appends the supplied array, which must be an array of the same
+ * type as this
, to the end of this
.
+ *
AbstractList
subclasses should update their
+ * modCount
after calling this method.
+ *
+ * @param ofArrayType the array to append
+ */
+ public void appendArray(Object ofArrayType) {
+ replaceSubArray(ofArrayType, size);
+ }
+
+ /**
+ * Set the array to the empty state, clearing all the data out and
+ * nulling objects (or "zero-ing" primitives.
+ *
Note: This method does not set modCountIncr
to
+ * 1
even though java.util.ArrayList
+ * would.
+ *
+ *
AbstractList
subclasses should update their
+ * modCount
after calling this method.
+ */
+ public void clear() {
+ modCountIncr = 0;
+ if ( size != 0) {
+ modCountIncr = 1;
+ clearRange(0, size - 1);
+ setSize(0);
+ }
+
+ }
+
+
+ /**
+ * Clears out the values in the specified range. For object arrays,
+ * the cleared range is nullified. For primitve arrays, it is
+ * "zero-ed" out. Note that the stop value is inclusive (not
+ * exclusive like some of the jdk equivalents).
+ *
Note: This method does not set modCountIncr
to
+ * 1
even though java.util.ArrayList
+ * would.
+ *
+ * @param start the start index, inclusive
+ * @param stop the stop index, inclusive
+ */
+ protected void clearRange(int start, int stop) {
+
+ if (start < stop && start >= 0 && stop <= size - 1) {
+ clearRangeInternal(start, stop);
+ } else {
+ if(start == stop && start >= 0 && stop <= size - 1) return;
+
+ throw new ArrayIndexOutOfBoundsException(
+ "start and stop must follow: 0 <= start <= stop <= " +
+ (size - 1) + ", but found start= " + start + " and stop=" + stop);
+ }
+ }
+
+ /**
+ * Used internally, no bounds checking.
+ *
+ * @param start the start index, inclusive
+ * @param stop the stop index, inclusive
+ */
+ private void clearRangeInternal(int start, int stop) {
+
+ Object base = getArray();
+ Class arrayType = base.getClass().getComponentType();
+ if (arrayType.isPrimitive()) {
+ if (arrayType == Boolean.TYPE) {
+ Arrays.fill((boolean[])base, start, stop + 1, false);
+ } else if (arrayType == Character.TYPE) {
+ Arrays.fill((char[])base, start, stop + 1, '\u0000');
+ } else if (arrayType == Byte.TYPE) {
+ Arrays.fill((byte[])base, start, stop + 1, (byte)0);
+ } else if (arrayType == Short.TYPE) {
+ Arrays.fill((short[])base, start, stop + 1, (short)0);
+ } else if (arrayType == Integer.TYPE) {
+ Arrays.fill((int[])base, start, stop + 1, 0);
+ } else if (arrayType == Long.TYPE) {
+ Arrays.fill((long[])base, start, stop + 1, 0);
+ } else if (arrayType == Float.TYPE) {
+ Arrays.fill((float[])base, start, stop + 1, 0.f);
+ } else if (arrayType == Double.TYPE) {
+ Arrays.fill((double[])base, start, stop + 1, 0.);
+ }
+ } else {
+ Arrays.fill((Object[])base, start, stop + 1, null);
+ }
+
+ }
+
+ /**
+ * Constructs and returns a simple array containing the same data as held
+ * in this growable array.
+ *
+ * @return array containing a shallow copy of the data.
+ */
+ public Object copyArray() {
+
+ Object copy = Array.newInstance(
+ getArray().getClass().getComponentType(), size);
+ System.arraycopy(getArray(), 0, copy, 0, size);
+ return copy;
+ }
+
+ /**
+ * Ensures that the base array has at least the specified
+ * minimum capacity.
+ *
AbstractList
subclasses should update their
+ * modCount
after calling this method.
+ *
+ * @param minCapacity new minimum size required
+ */
+ protected void ensureCapacity(int minCapacity) {
+ // ArrayList always increments the mod count, even if no
+ // structural change is made (not sure why).
+ // This only indicates a mod count change if a change is made.
+ modCountIncr = 0;
+ if (minCapacity > capacity) {
+ modCountIncr = 1;
+ int newCapacity = (capacity * 2) + 1;
+ newCapacity = (newCapacity < minCapacity)
+ ? minCapacity
+ : newCapacity;
+ setNewBase(newCapacity);
+ capacity = newCapacity;
+ }
+ }
+
+ /**
+ * Gets the next add position for appending a value to those in the array.
+ * If the underlying array is full, it is grown by the appropriate size
+ * increment so that the index value returned is always valid for the
+ * array in use by the time of the return.
+ *
AbstractList
subclasses should update their
+ * modCount
after calling this method.
+ *
+ * @return index position for next added element
+ */
+ protected int getAddIndex() {
+ int index = size++;
+ if (size > capacity) {
+ ensureCapacity(size);
+ }
+ return index;
+ }
+
+ /**
+ * Get the backing array. This method is used by the type-agnostic base
+ * class code to access the array used for type-specific storage by the
+ * child class.
+ *
+ * @return backing array object
+ */
+ protected abstract Object getArray();
+
+ protected boolean isEmpty() {
+ return size == 0;
+ }
+
+ /**
+ * Makes room to insert a value at a specified index in the array.
+ *
AbstractList
subclasses should update their
+ * modCount
after calling this method.
+ *
+ * @param index index position at which to insert element
+ */
+ protected void makeInsertSpace(int index) {
+ makeInsertSpace(index, 1);
+ }
+// protected void makeInsertSpace(int index) {
+//
+// modCountIncr = 0;
+// if (index >= 0 && index <= size) {
+// if (++size > capacity) {
+// ensureCapacity(size);
+// }
+// if (index < size - 1) {
+// modCountIncr = 1;
+// Object array = getArray();
+// System.arraycopy(array, index, array, index + 1,
+// size - index - 1);
+// }
+// } else {
+// throw new ArrayIndexOutOfBoundsException(
+// "Index must be between 0 and " +
+// size + ", but was " + index);
+// }
+// }
+
+ protected void makeInsertSpace(int index, int length) {
+
+ modCountIncr = 0;
+ if (index >= 0 && index <= size) {
+ int toCopy = size - index;
+ size = size + length;
+ // First increase array size if needed
+ if (size > capacity) {
+ ensureCapacity(size);
+ }
+ if (index < size - 1) {
+ modCountIncr = 1;
+ Object array = getArray();
+ System.arraycopy(array, index, array, index + length, toCopy);
+ }
+ } else {
+ throw new ArrayIndexOutOfBoundsException(
+ "Index must be between 0 and " +
+ size + ", but was " + index);
+ }
+ }
+
+ /**
+ * Remove a value from the array. All values above the index removed
+ * are moved down one index position.
+ *
AbstractList
subclasses should always increment
+ * their modCount
method after calling this, as
+ * remove
always causes a structural modification.
+ *
+ * @param index index number of value to be removed
+ */
+ public void remove(int index) {
+ if (index >= 0 && index < size) {
+ size = size - 1;
+ if (index < size){
+ Object base = getArray();
+ System.arraycopy(base, index + 1, base, index, size - index);
+ clearRangeInternal(size, size);
+ }
+
+ } else {
+ if (size == 0) {
+ throw new IllegalStateException(
+ "Cannot remove data from an empty array");
+ }
+ throw new IndexOutOfBoundsException("Index must be between 0 and " +
+ (size - 1) + ", but was " + index);
+
+ }
+ }
+
+ public void remove(int start, int stop) {
+ if (start >= 0 && stop <= size && start <= stop) {
+ Object base = getArray();
+ int nRemove = stop - start + 1;
+ System.arraycopy(base, stop + 1, base, start, size - stop - 1);
+ size = size - nRemove;
+ clearRangeInternal(size, size + nRemove - 1);
+ setArray(base);
+ return;
+ }
+
+ throw new IndexOutOfBoundsException("start and stop must follow: 0 <= start <= stop <= " +
+ (size - 1) + ", but found start= " + start + " and stop=" + stop);
+ }
+
+ /**
+ * Allows an array type to overwrite a segment of the array.
+ * Will expand the array if (atIndex + 1) + ofArrayType
's length
+ * is greater than the current length.
+ *
AbstractList
subclasses should update their
+ * modCount
after calling this method.
+ *
+ * @param array
+ * @param atIndex
+ */
+ public void replaceSubArray(Object array, int atIndex) {
+
+ modCountIncr = 0;
+ if( !array.getClass().isArray() ) {
+ throw new IllegalArgumentException("'array' must be an array type");
+ }
+
+ if (atIndex < 0 || atIndex > size) {
+ String message = (atIndex < 0) ?
+ "Index cannot be negative" :
+ "Index of " + atIndex + " must not exceed current size of " + size;
+
+ throw new ArrayIndexOutOfBoundsException(message);
+ }
+
+ int nAddLength = Array.getLength(array);
+
+ int requiredLength = atIndex + nAddLength;
+
+ if( requiredLength > capacity) {
+ // need to expand to size atIndex + nAddLength
+ ensureCapacity(requiredLength);
+ }
+
+ size = Math.max(size, requiredLength);
+ try {
+ modCountIncr = 1;
+ System.arraycopy(array, 0, getArray(), atIndex, nAddLength);
+ } catch(ArrayStoreException e) {
+ throw new IllegalArgumentException(
+ "'ofArrayType' must be compatible with existing array type of " +
+ getArray().getClass().getName() + "\tsee java.lang.Class.getName().");
+ }
+ }
+
+ /**
+ * Set the backing array. This method is used by the type-agnostic base
+ * class code to set the array used for type-specific storage by the
+ * child class.
+ *
+ * @param array the backing array object
+ */
+ protected abstract void setArray(Object array);
+
+ /**
+ * Replaces the existing base array in the subclass with a new
+ * base array resized to the specified capacity.
+ * @param newCapacity
+ */
+ private void setNewBase(int newCapacity) {
+ modCountIncr = 1;
+ Object base = getArray();
+ Class baseType = base.getClass().getComponentType();
+ Object newBase = Array.newInstance(baseType, newCapacity);
+ System.arraycopy(base, 0, newBase, 0, capacity);
+ setArray(newBase);
+ }
+
+ /**
+ * Sets the number of values currently present in the array. If the new
+ * size is greater than the current size, the added values are initialized
+ * to the default values. If the new size is less than the current size,
+ * all values dropped from the array are discarded.
+ *
AbstractList
subclasses should update their
+ * modCount
after calling this method.
+ * @param count number of values to be set
+ */
+ public void setSize(int count) {
+ if (count > capacity) {
+ ensureCapacity(count);
+ } else if (count < size) {
+ clearRange(count, size);
+ }
+ size = count;
+ }
+
+ /**
+ * Get the number of values currently present in the array.
+ *
+ * @return count of values present
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Provides a default comma-delimited representation of array.
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("[");
+
+ Object base = getArray();
+ Class arrayType = base.getClass().getComponentType();
+ int n = size - 1;
+ if (arrayType.isPrimitive()) {
+ for (int i = 0; i < n ; i++) {
+ buf.append(Array.get(base, i)).append(", ");
+ }
+ if(n >= 0) buf.append(Array.get(base, n));
+ } else {
+ Object[] objects = (Object[])base;
+ for (int i = 0; i < n ; i++) {
+ buf.append(objects[i]).append(", ");
+ }
+ if(n >= 0) buf.append(objects[n]);
+ }
+ buf.append("]");
+ return buf.toString();
+ }
+
+
+ /**
+ * Removes any excess capacity in the backing array so it is
+ * just big enough to hold the amount of data actually in the array.
+ */
+ protected void trimToSize() {
+ // Don't need to adjust modCountIncr since AbstractList subclasses
+ // should only ever see up to the size (and not the capacity--which
+ // is encapsulated).
+ if(size < capacity) {
+ setNewBase(size);
+ }
+ }
+
+
+ /**
+ * Returns the modification count increment, which is used by
+ * AbstractList
subclasses to adjust modCount
+ * AbstractList
uses it's modCount
field
+ * to invalidate concurrent operations (like iteration) that should
+ * fail if the underlying array changes structurally during the
+ * operation.
+ *
+ * @return the modification count increment (0 if no change, 1 if changed)
+ */
+ public int getModCountIncr() {
+ return modCountIncr;
+ }
+}
Index: PyArray.java
===================================================================
RCS file: /cvsroot/jython/jython/org/python/core/PyArray.java,v
retrieving revision 2.10
diff -u -r2.10 PyArray.java
--- PyArray.java 22 Feb 2005 04:19:30 -0000 2.10
+++ PyArray.java 14 Mar 2005 01:01:09 -0000
@@ -1,7 +1,8 @@
-// Copyright (c) Corporation for National Research Initiatives
+// Copyright (c) Corporation for National Research Initiatives
package org.python.core;
import java.lang.reflect.Array;
+
/**
* A wrapper class around native java arrays.
*
@@ -10,26 +11,87 @@
*
* See also the jarray module. */ - -public class PyArray extends PySequence { - Object data; - Class type; - +public class PyArray extends PySequence implements Cloneable { + + protected Object data; + protected Class type; + protected String typecode; + protected ArrayDelegate delegate; + + // PyArray can't extend anymore, so delegate + private static class ArrayDelegate extends ArrayHelper { + + final PyArray pyArray; + + private ArrayDelegate(PyArray pyArray) { + super((pyArray.data == null) + ? 0 + : Array.getLength(pyArray.data)); + this.pyArray = pyArray; + } + + protected Object getArray() { + return pyArray.data; + } + protected void setArray(Object array) { + pyArray.data = array; + } + + protected void makeInsertSpace(int index) { + super.makeInsertSpace(index, 1); + } + + protected void makeInsertSpace(int index, int length) { + super.makeInsertSpace(index, length); + } + + public void remove(int index) { + super.remove(index); + } + } + + private PyArray() { + // do nothing, shell instance + } + + public PyArray(PyArray toCopy) { + + data = toCopy.delegate.copyArray(); + delegate = new ArrayDelegate(this); + type = toCopy.type; + } + public PyArray(Class type, Object data) { this.type = type; this.data = data; + delegate = new ArrayDelegate(this); } public PyArray(Class type, int n) { this(type, Array.newInstance(type, n)); } + public static PyArray zeros(int n, char typecode) { + PyArray array = zeros(n, char2class(typecode)); + array.typecode = Character.toString(typecode); + return array; + } + public static PyArray zeros(int n, Class ctype) { - return new PyArray(ctype, n); + PyArray array = new PyArray(ctype, n); + array.typecode = ctype.getName(); + return array; } + public static PyArray array(PyObject seq, char typecode) { + PyArray array = PyArray.array(seq, char2class(typecode)); + array.typecode = Character.toString(typecode); + return array; + } + public static PyArray array(PyObject seq, Class ctype) { PyArray array = new PyArray(ctype, seq.__len__()); + array.typecode = ctype.getName(); PyObject iter = seq.__iter__(); PyObject item = null; for (int i = 0; (item = iter.__iternext__()) != null; i++) { @@ -38,6 +100,10 @@ return array; } + public Object getArray() { + return delegate.copyArray(); + } + public static Class char2class(char type) { switch (type) { case 'z': @@ -72,7 +138,7 @@ } public int __len__() { - return Array.getLength(data); + return delegate.getSize(); } protected PyObject get(int i) { @@ -80,8 +146,7 @@ } protected PyObject getslice(int start, int stop, int step) { - if (step > 0 && stop < start) - stop = start; + if (step > 0 && stop < start) stop = start; int n = sliceLength(start, stop, step); PyArray ret = new PyArray(type, n); @@ -89,10 +154,8 @@ System.arraycopy(data, start, ret.data, 0, n); return ret; } - int j = 0; - for (int i = start; j < n; i += step) { + for (int i = start, j = 0; j < n; i += step, j++) { Array.set(ret.data, j, Array.get(data, i)); - j++; } return ret; } @@ -102,10 +165,126 @@ } protected void del(int i) { - throw Py.TypeError("can't remove from array"); + //Now the ArrayHelper can support this: + //throw Py.TypeError("can't remove from array"); + delegate.remove(i); + } + + public PyObject count(PyObject value) { + // note: cpython does not raise type errors based on item type + int iCount = 0; + for (int i = 0; i < delegate.getSize(); i++) { + if (value.equals(Py.java2py(Array.get(data, i)))) iCount++; + } + return new PyInteger(iCount); } + + private int indexInternal(PyObject value) { + // note: cpython does not raise type errors based on item type + for (int i = 0; i < delegate.getSize(); i++) { + if (value.equals(Py.java2py(Array.get(data, i)))) { + return i; + } + } + return -1; + } + + public PyObject index(PyObject value) { + + int index = indexInternal(value); + if(index != -1) return new PyInteger(index); + + throw Py.ValueError("array.index(" + value + "): " + value + + " not found in array"); + } + + + public void remove(PyObject value) { + int index = indexInternal(value) ; + if(index != -1) { + delegate.remove(index); + return; + } + + throw Py.ValueError("array.remove(" + value + "): " + + value + " not found in array"); + } + + public PyObject __add__(PyObject other) { + PyArray otherArr = null; + if (!(other instanceof PyArray)) { + throw Py.TypeError("can only append another array to an array"); + } + otherArr = (PyArray)other; + if (!otherArr.type.equals(this.type)) { + throw Py.TypeError( + "can only append arrays of the same type, " + + "expected '" + this.type + ", found " + otherArr.type); + } + PyArray ret = new PyArray(this); + ret.delegate.appendArray(otherArr.delegate.copyArray()); + return ret; + } + + public void append(PyObject value) { + // Currently, this is asymmetric with extend, which + // *will* do conversions like append(5.0) to an int array. + // Also, cpython 2.2 will do the append coersion. However, + // it is deprecated in cpython 2.3, so maybe we are just + // ahead of our time ;-) + Object o = Py.tojava(value, type); + int afterLast = delegate.getSize(); + delegate.makeInsertSpace(afterLast); + Array.set(data, afterLast, o); + } + + public void extend(PyObject array) { + PyArray otherArr = null; + if (!(array instanceof PyArray)) { + throw Py.TypeError("can only extend an array witn another array"); + } + otherArr = (PyArray)array; + if (!otherArr.type.equals(this.type)) { + throw Py.TypeError( + "can only extend with an array of the same type, " + + "expected '" + this.type + ", found " + otherArr.type); + } + delegate.appendArray(otherArr.delegate.copyArray()); + } + + public void insert(int index, PyObject value) { + delegate.makeInsertSpace(index); + Array.set(data, index, Py.tojava(value, type)); + } + + public PyObject pop() { + return pop(-1); + } + + public PyObject pop(int index) { + // todo: python-style error handling + index = (index < 0) + ? delegate.getSize() + index + : index; + PyObject ret = Py.java2py(Array.get(data, index)); + delegate.remove(index); + return ret; + } + protected void delRange(int start, int stop, int step) { - throw Py.TypeError("can't remove from array"); + // Now the ArrayHelper can support this: + //throw Py.TypeError("can't remove from array"); + if (step > 0 && stop < start) stop = start; + + if (step == 1) { + delegate.remove(start, stop); + } else { + int n = sliceLength(start, stop, step); + + for (int i = start, j = 0; j < n; i += step, j++) { + delegate.remove(i); + } + } } protected void set(int i, PyObject value) { @@ -114,14 +293,30 @@ } protected void setslice(int start, int stop, int step, PyObject value) { - if (value instanceof PyString && type == Character.TYPE) { - char[] chars = value.toString().toCharArray(); - if (chars.length == stop-start && step == 1) { - System.arraycopy(chars, 0, data, start, chars.length); - } else { - throw Py.ValueError("invalid bounds for setting from string"); - } - + if(type == Character.TYPE && value instanceof PyString) { + char[] chars = null; + //if (value instanceof PyString) { + if (step != 1) { + throw Py.ValueError( + "invalid bounds for setting from string"); + } + chars = value.toString().toCharArray(); + + //} +// else if (value instanceof PyArray && +// ((PyArray)value).type == Character.TYPE) { +// PyArray other = (PyArray)value; +// chars = (char[])other.delegate.copyArray(); +// } + int insertSpace = chars.length - (stop - start); + // adjust the array, either adding space or removing space + if(insertSpace > 0) { + delegate.makeInsertSpace(start, insertSpace); + } else if (insertSpace < 0) { + delegate.remove(start, -insertSpace + start - 1); + } + delegate.replaceSubArray(chars, start); + } else { if (value instanceof PyString && type == Byte.TYPE) { byte[] chars = value.toString().getBytes(); @@ -131,14 +326,38 @@ throw Py.ValueError( "invalid bounds for setting from string"); } - } else { - throw Py.TypeError( - "can't set slice in array (except from string)"); + } else if(value instanceof PyArray){ + PyArray array = (PyArray)value; + int insertSpace = array.delegate.getSize() - (stop - start); + // adjust the array, either adding space or removing space + // ...snapshot in case "value" is "this" + Object arrayCopy = array.delegate.copyArray(); + if(insertSpace > 0) { + delegate.makeInsertSpace(start, insertSpace); + } else if (insertSpace < 0) { + delegate.remove(start, -insertSpace + start - 1); + } + try { + delegate.replaceSubArray(arrayCopy, start); + } catch (IllegalArgumentException e) { + throw Py.TypeError("Slice typecode '" + array.typecode + + "' is not compatible with this array (typecode '" + + this.typecode + "')"); + } } } } + public void reverse() { + // build a new reversed array and set this.data to it when done + Object array = Array.newInstance(type, Array.getLength(data)); + for(int i = 0, lastIndex = delegate.getSize() - 1; i <= lastIndex; i++) { + Array.set(array, lastIndex - i, Array.get(data, i)); + } + data = array; + } + public String tostring() { if (type == Character.TYPE) { return new String((char[])data); @@ -149,6 +368,10 @@ throw Py.TypeError( "can only convert arrays of byte and char to string"); } + + public Object clone() { + return new PyArray(this); + } public PyString __repr__() { StringBuffer buf = new StringBuffer("array(["); @@ -164,4 +387,8 @@ return new PyString(buf.toString()); } + + public String getTypecode() { + return typecode; + } } Index: PySequence.java =================================================================== RCS file: /cvsroot/jython/jython/org/python/core/PySequence.java,v retrieving revision 2.21 diff -u -r2.21 PySequence.java --- PySequence.java 22 Feb 2005 04:19:31 -0000 2.21 +++ PySequence.java 14 Mar 2005 01:02:26 -0000 @@ -480,7 +480,7 @@ array.set(i, o); } //System.out.println("getting: "+component+", "+array.data); - return array.data; + return array.getArray(); } catch (Throwable t) { ;//System.out.println("failed to get: "+component.getName()); } Index: jarray.java =================================================================== RCS file: /cvsroot/jython/jython/org/python/modules/jarray.java,v retrieving revision 2.2 diff -u -r2.2 jarray.java --- jarray.java 28 Oct 2001 17:13:44 -0000 2.2 +++ jarray.java 14 Mar 2005 01:02:54 -0000 @@ -4,14 +4,14 @@ public class jarray { public static PyArray array(PyObject seq, char typecode) { - return PyArray.array(seq, PyArray.char2class(typecode)); + return PyArray.array(seq, typecode); } public static PyArray array(PyObject seq, Class type) { return PyArray.array(seq, type); } public static PyArray zeros(int n, char typecode) { - return PyArray.zeros(n, PyArray.char2class(typecode)); + return PyArray.zeros(n, typecode); } public static PyArray zeros(int n, Class type) { Index: test_jarray.py =================================================================== RCS file: /cvsroot/jython/jython/Lib/test/test_jarray.py,v retrieving revision 1.1 diff -u -r1.1 test_jarray.py --- test_jarray.py 15 Feb 1999 14:54:08 -0000 1.1 +++ test_jarray.py 14 Mar 2005 01:03:26 -0000 @@ -1,3 +1,6 @@ +# CAU: adaptation of the cpython 2.2 test_array.py for jython 2.2 +# combined with the jarray test from 2.1 + from test_support import * print_test('jarray module (test_jarray.py)', 1) @@ -19,3 +22,193 @@ awt.Color.RGBtoHSB(0,255,255, hsb1) #print hsb, hsb1 assert hsb == hsb1 + +def main(): + + testtype('c', 'c') + + for type in (['b', 'h', 'i', 'l', 'f', 'd']): + testtype(type, 1) + +#CAU: nyi unlink(TESTFN) + + +def testoverflow(type, lowerLimit, upperLimit): + # should not overflow assigning lower limit + if verbose: + print "overflow test: array(%s, [%s])" % (`lowerLimit`, `type`) + try: + a = array([lowerLimit], type) + except: + raise TestFailed, "array(%s) overflowed assigning %s" %\ + (`lowerLimit`, `type`) + # should overflow assigning less than lower limit + if verbose: + print "overflow test: array(%s, [%s])" % (`lowerLimit-1`, `type`) + try: + a = array([lowerLimit-1], type) + raise TestFailed, "array(%s) did not overflow assigning %s" %\ + (`lowerLimit-1`, `type`) + except OverflowError: + pass + # should not overflow assigning upper limit + if verbose: + print "overflow test: array(%s, [%s])" % (`upperLimit`, `type`) + try: + a = array([upperLimit], type) + except: + raise TestFailed, "array(%s) overflowed assigning %s" %\ + (`upperLimit`, `type`) + # should overflow assigning more than upper limit + if verbose: + print "overflow test: array(%s, [%s])" % (`upperLimit+1`, `type`) + try: + a = array([upperLimit+1], type) + raise TestFailed, "array(%s) did not overflow assigning %s" %\ + (`upperLimit+1`, `type`) + except OverflowError: + pass + + + +def testtype(type, example): + + print "testing type ", type + + a = array([], type) + a.append(example) + if verbose: + print 40*'*' + print 'array after append: ', a + a.typecode +#CAU: nyi a.itemsize + +#CAU: nyi if a.typecode in ('i', 'b', 'h', 'l'): +#CAU: nyi a.byteswap() + +#CAU: nyi if a.typecode == 'c': +#CAU: nyi f = open(TESTFN, "w") +#CAU: nyi f.write("The quick brown fox jumps over the lazy dog.\n") +#CAU: nyi f.close() +#CAU: nyi f = open(TESTFN, 'r') +#CAU: nyi a.fromfile(f, 10) +#CAU: nyi f.close() +#CAU: nyi if verbose: +#CAU: nyi print 'char array with 10 bytes of TESTFN appended: ', a +#CAU: nyi a.fromlist(['a', 'b', 'c']) +#CAU: nyi if verbose: +#CAU: nyi print 'char array with list appended: ', a + +#CAU: nyi a.insert(0, example) +#CAU: nyi if verbose: +#CAU: nyi print 'array of %s after inserting another:' % a.typecode, a +#CAU: nyi f = open(TESTFN, 'w') +#CAU: nyi a.tofile(f) +#CAU: nyi f.close() + +#CAU: nyi # This block is just to verify that the operations don't blow up. +#CAU: nyi a.tolist() +#CAU: nyi a.tostring() + repr(a) + str(a) + +#CAU: nyi if verbose: +#CAU: nyi print 'array of %s converted to a list: ' % a.typecode, a.tolist() +#CAU: nyi if verbose: +#CAU: jython 2.1 PyArray only supported tostring() on byte and char arrays +#CAU: nyi print 'array of %s converted to a string: ' \ +#CAU: nyi % a.typecode, `a.tostring()` + + if type == 'c': + a = array("abcde", type) + a[:-1] = a + if a != array("abcdee", type): + raise TestFailed, "array(%s) self-slice-assign (head)" % `type` + a = array("abcde", type) + a[1:] = a + if a != array("aabcde", type): + raise TestFailed, "array(%s) self-slice-assign (tail)" % `type` + a = array("abcde", type) + a[1:-1] = a + if a != array("aabcdee", type): + raise TestFailed, "array(%s) self-slice-assign (cntr)" % `type` + if a.index("e") != 5: + raise TestFailed, "array(%s) index-test" % `type` + if a.count("a") != 2: + raise TestFailed, "array(%s) count-test" % `type` + a.remove("e") + if a != array("aabcde", type): + raise TestFailed, "array(%s) remove-test" % `type` + if a.pop(0) != "a": + raise TestFailed, "array(%s) pop-test" % `type` + if a.pop(1) != "b": + raise TestFailed, "array(%s) pop-test" % `type` + a.extend(array("xyz", type)) + if a != array("acdexyz", type): + raise TestFailed, "array(%s) extend-test" % `type` + a.pop() + a.pop() + a.pop() + x = a.pop() + if x != 'e': + raise TestFailed, "array(%s) pop-test" % `type` + if a != array("acd", type): + raise TestFailed, "array(%s) pop-test" % `type` + a.reverse() + if a != array("dca", type): + raise TestFailed, "array(%s) reverse-test" % `type` + else: + a = array([1, 2, 3, 4, 5], type) + a[:-1] = a + if a != array([1, 2, 3, 4, 5, 5], type): + raise TestFailed, "array(%s) self-slice-assign (head)" % `type` + a = array([1, 2, 3, 4, 5], type) + a[1:] = a + if a != array([1, 1, 2, 3, 4, 5], type): + raise TestFailed, "array(%s) self-slice-assign (tail)" % `type` + a = array([1, 2, 3, 4, 5], type) + a[1:-1] = a + if a != array([1, 1, 2, 3, 4, 5, 5], type): + raise TestFailed, "array(%s) self-slice-assign (cntr)" % `type` + if a.index(5) != 5: + raise TestFailed, "array(%s) index-test" % `type` + if a.count(1) != 2: + raise TestFailed, "array(%s) count-test" % `type` + a.remove(5) + if a != array([1, 1, 2, 3, 4, 5], type): + raise TestFailed, "array(%s) remove-test" % `type` + if a.pop(0) != 1: + raise TestFailed, "array(%s) pop-test" % `type` + if a.pop(1) != 2: + raise TestFailed, "array(%s) pop-test" % `type` + a.extend(array([7, 8, 9], type)) + if a != array([1, 3, 4, 5, 7, 8, 9], type): + raise TestFailed, "array(%s) extend-test" % `type` + a.pop() + a.pop() + a.pop() + x = a.pop() + if x != 5: + raise TestFailed, "array(%s) pop-test" % `type` + if a != array([1, 3, 4], type): + raise TestFailed, "array(%s) pop-test" % `type` + a.reverse() + if a != array([4, 3, 1], type): + raise TestFailed, "array(%s) reverse-test" % `type` + + # test that overflow exceptions are raised as expected for assignment + # to array of specific integral types +#CAU: nyi from math import pow +#CAU: nyi if type in ('b', 'h', 'i', 'l'): +#CAU: nyi # check signed and unsigned versions +#CAU: nyi a = array(type) +#CAU: nyi signedLowerLimit = -1 * long(pow(2, a.itemsize * 8 - 1)) +#CAU: nyi signedUpperLimit = long(pow(2, a.itemsize * 8 - 1)) - 1L +#CAU: nyi unsignedLowerLimit = 0 +#CAU: nyi unsignedUpperLimit = long(pow(2, a.itemsize * 8)) - 1L +#CAU: nyi testoverflow(type, signedLowerLimit, signedUpperLimit) +#CAU: nyi testoverflow(type.upper(), unsignedLowerLimit, unsignedUpperLimit) + + + +main()