Index: src/org/python/core/PyJavaType.java =================================================================== --- src/org/python/core/PyJavaType.java (revision 5617) +++ src/org/python/core/PyJavaType.java (working copy) @@ -4,6 +4,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -319,12 +320,47 @@ return new ListGetProxy(getType(), self, info); } + private PyObject getItem(int key) { + List p = (List)self.javaProxy; + int k = key < 0 ? key + p.size() : key; + try { + return Py.java2py(p.get(k)); + } catch (IndexOutOfBoundsException exc) { + throw Py.IndexError("index out of range: " + String.valueOf(key)); + } + } + + private PyObject getSlice(PySlice slice) { + List p = (List)self.javaProxy; + int indices[] = slice.indicesEx(p.size()); + int start = indices[0], stop = indices[1], step = indices[2], n = indices[3]; + if(step > 0 && stop < start) { + stop = start; + } + PyObject[] newList = new PyObject[n]; + if(step == 1) { + for (int i = start; i < stop; i++) + { + newList[i-start] = Py.java2py(p.get(i)); + } + return new PyList(newList); + } + int j = 0; + for(int i = start; j < n; i += step) { + newList[j] = Py.java2py(p.get(i)); + j++; + } + return new PyList(newList); + } + @Override public PyObject __call__(PyObject key) { if (key instanceof PyInteger) { - return Py.java2py(((List)self.javaProxy).get(((PyInteger)key).getValue())); + return getItem(((PyInteger)key).getValue()); + } else if (key instanceof PySlice) { + return getSlice((PySlice)key); } else { - throw Py.TypeError("only integer keys accepted"); + throw Py.TypeError("only integer or slice keys accepted"); } } } @@ -343,13 +379,102 @@ return new ListSetProxy(getType(), self, info); } + @SuppressWarnings("unchecked") + private void setItem(int key, PyObject value) { + List p = (List)self.javaProxy; + int k = key < 0 ? key + p.size() : key; + try { + p.set(k, Py.tojava(value, Object.class)); + } catch (IndexOutOfBoundsException exc) { + throw Py.IndexError("assignment index out of range: " + String.valueOf(key)); + } + } + + private void setSlice(PySlice slice, PyObject value) { + List p = (List)self.javaProxy; + int indices[] = slice.indicesEx(p.size()); + int start = indices[0], stop = indices[1], step = indices[2], n = indices[3]; + if(stop < start) { + stop = start; + } + if(value instanceof PySequence) { + PySequence sequence = (PySequence)value; + setslicePySequence(slice, start, stop, step, n, sequence); + } else if(value instanceof List) { + List list = (List)value.__tojava__(List.class); + if(list != null && list != Py.NoConversion) { + setsliceList(slice, start, stop, step, list); + } + } else { + setsliceIterable(slice, start, stop, step, n, value); + } + } + + @SuppressWarnings("unchecked") + private void setslicePySequence(PySlice slice, int start, int stop, int step, int n, PySequence value) { + List p = (List)self.javaProxy; + if(slice.step != Py.None) { + if(n != value.__len__()) { + throw Py.ValueError("attempt to assign sequence of size " + value.__len__() + " to extended slice of size " + n); + } + } + if(step == 1) { + PyObject[] srcArray; + Object[] jArray; + + if(value instanceof PySequenceList) { + srcArray = ((PySequenceList)value).getArray(); + } else { + srcArray = Py.unpackSequence(value, value.__len__()); + } + jArray = new Object[srcArray.length]; + for (int i = 0; i < srcArray.length; i++) { + jArray[i] = Py.tojava(srcArray[i], Object.class); + } + List sub = p.subList(start, stop); + sub.clear(); + p.addAll( start, Arrays.asList(jArray) ); + } else if(step != 0) { + for (int i = 0, j = start; i < n; i++, j += step) { + p.set(j, Py.tojava(value.pyget(i), Object.class)); + } + } + } + + @SuppressWarnings("unchecked") + private void setsliceList(PySlice slice, int start, int stop, int step, List value) { + List p = (List)self.javaProxy; + if(step != 1) { + throw Py.TypeError("setslice with java.util.List and step != 1 not supported yet"); + } + int n = value.size(); + for(int i = 0; i < n; i++) { + p.add(i + start, value.get(i)); + } + } + + protected void setsliceIterable(PySlice slice, int start, int stop, int step, int n, PyObject value) { + PyObject[] seq; + try { + seq = Py.make_array(value); + } catch (PyException pye) { + if (Py.matchException(pye, Py.TypeError)) { + throw Py.TypeError("can only assign an iterable"); + } + throw pye; + } + setslicePySequence(slice, start, stop, step, n, new PyList(seq)); + } + @Override public PyObject __call__(PyObject key, PyObject value) { if (key instanceof PyInteger) { - ((List)self.javaProxy).set(((PyInteger)key).getValue(), - Py.tojava(value, Object.class)); + setItem(((PyInteger)key).getValue(), value); + } else if (key instanceof PySlice) { + PySlice slice = (PySlice)key; + setSlice(slice, value); } else { - throw Py.TypeError("only integer keys accepted"); + throw Py.TypeError("only integer or slice keys accepted"); } return Py.None; } @@ -369,13 +494,47 @@ return new ListRemoveProxy(getType(), self, info); } + private void delItem(int key) { + List p = (List)self.javaProxy; + int k = key < 0 ? key + p.size() : key; + try { + p.remove(k); + } catch (IndexOutOfBoundsException exc) { + throw Py.IndexError("assignment index out of range: " + String.valueOf(key)); + } + } + + private void delSlice(PySlice slice) { + List p = (List)self.javaProxy; + int indices[] = slice.indicesEx(p.size()); + int start = indices[0], stop = indices[1], step = indices[2]; + if(step == 1) { + for (int i = stop - 1; i >= start; i--) { + p.remove(i); + } + } else if(step > 1) { + for(int i = start; i < stop; i += step) { + p.remove(i); + i--; + stop--; + } + } else if(step < 0) { + for(int i = start; i >= 0 && i >= stop; i += step) { + p.remove(i); + } + } + } + @Override - public PyObject __call__(PyObject key, PyObject value) { + public PyObject __call__(PyObject key) { if (key instanceof PyInteger) { - return Py.java2py(((List)self.javaProxy).remove(((PyInteger)key).getValue())); + delItem(((PyInteger)key).getValue()); + } else if (key instanceof PySlice) { + delSlice((PySlice)key); } else { throw Py.TypeError("only integer keys accepted"); } + return Py.None; } } Index: src/org/python/core/PyList.java =================================================================== --- src/org/python/core/PyList.java (revision 5617) +++ src/org/python/core/PyList.java (working copy) @@ -114,7 +114,9 @@ protected void delRange(int start, int stop, int step) { if(step == 1) { - remove(start, stop); + if (stop > start) { + remove(start, stop); + } } else if(step > 1) { for(int i = start; i < stop; i += step) { remove(i); @@ -164,13 +166,7 @@ otherArray = otherArray.clone(); } list.replaceSubArray(start, stop, otherArray, 0, n); - } else if(step > 1) { - int n = value.__len__(); - for(int i = 0, j = 0; i < n; i++, j += step) { - list.pyset(j + start, value.pyget(i)); - } - } else if(step < 0) { - int n = value.__len__(); + } else if(step != 0) { if(value == this) { PyList newseq = new PyList(); PyObject iter = value.__iter__(); @@ -179,7 +175,8 @@ } value = newseq; } - for(int i = 0, j = list.size() - 1; i < n; i++, j += step) { + int n = value.__len__(); + for (int i = 0, j = start; i < n; i++, j += step) { list.pyset(j, value.pyget(i)); } } Index: test_java_proxy_PyJavaType.py =================================================================== --- test_java_proxy_PyJavaType.py (revision 0) +++ test_java_proxy_PyJavaType.py (revision 0) @@ -0,0 +1,245 @@ +from java.util import ArrayList, Vector + +from copy import copy + +import unittest +import test.test_support + +class CollectionProxyTest(unittest.TestCase): + def _perform_op(self, value, op_func): + """ + Perform an operation + + value - the value to operate on + op_func - the function that applies the operation to value + + Returns: + the result of calling op_func, OR the exception that was raised in op_func + """ + try: + return op_func(value) + except Exception, e: + return type( e ) + + + def _list_op_test(self, initial_value, op_func): + """ + Tests a list operation + + Ensures that performing an operation on: + - a python list + - a java.util.List instance + + givens the same result in both cases + """ + # Take a copy of the initial value + py_list = copy(initial_value) + + # Produce the same value as a java.util.ArrayList + j_list = ArrayList() + j_list.addAll(initial_value) + + # Produce the same value as a java.util.Vector + j_vec = Vector() + j_vec.addAll(initial_value) + + # Perform the operation on both + py_result = self._perform_op(py_list, op_func) + jlist_result = self._perform_op(j_list, op_func) + jvec_result = self._perform_op(j_vec, op_func) + + # Compare the results of the python list and the ArrayList + if py_result != jlist_result: + print 'Python AND ArrayList RESULTS DIFFER:' + print py_result + print jlist_result + self.assert_(py_result == jlist_result) + + # Compare the results of the python list and the Vector + if py_result != jvec_result: + print 'Python AND Vector RESULTS DIFFER:' + print py_result + print jvec_result + self.assert_(py_result == jvec_result) + + # Compare the python list and the ArrayList + j_list = list(j_list) + if py_list != j_list: + print 'Python and ArrayList DIFFER:' + print py_list + print j_list + self.assert_( py_list == j_list ) + + # Compare the python list and the Vector + j_vec = list(j_vec) + if py_list != j_vec: + print 'Python and Vector DIFFER:' + print py_list + print j_vec + self.assert_( py_list == j_vec ) + + + + + def test_get_integer(self): + initial_value = range(0,5) + + def make_op_func(index): + return lambda xs: xs[index] + + for i in xrange( -7, 7 ): + self._list_op_test( initial_value, make_op_func( i ) ) + + + + def test_get_slice(self): + initial_value = range(0,10) + + def make_op_func(i, j, k): + return lambda xs: xs[i:j:k] + + for i in xrange( -12, 12 ): + for j in xrange( -12, 12 ): + for k in xrange( -12, 12 ): + self._list_op_test( initial_value, make_op_func( i, j, k ) ) + + + + def test_set_integer(self): + initial_value = range(0,5) + + def make_op_func(index): + def _f(xs): + xs[index] = 100 + return _f + + for i in xrange( -7, 7 ): + self._list_op_test( initial_value, make_op_func( i ) ) + + + def test_set_slice(self): + initial_value = range(0,10) + + def make_op_func(i, j, k, v): + def _f(xs): + xs[i:j:k] = v + return _f + + for i in xrange( -12, 12 ): + for j in xrange( -12, 12 ): + for k in xrange( -12, 12 ): + self._list_op_test( initial_value, make_op_func( i, j, k, [] ) ) + self._list_op_test( initial_value, make_op_func( i, j, k, range(0,2) ) ) + self._list_op_test( initial_value, make_op_func( i, j, k, range(0,4) ) ) + self._list_op_test( initial_value, make_op_func( i, j, k, xrange(0,2) ) ) + + + + def test_del_integer(self): + initial_value = range(0,5) + + def make_op_func(index): + def _f(xs): + del xs[index] + return _f + + for i in xrange( -7, 7 ): + self._list_op_test( initial_value, make_op_func( i ) ) + + + def test_del_slice(self): + initial_value = range(0,10) + + def make_op_func(i, j, k): + def _f(xs): + del xs[i:j:k] + return _f + + for i in xrange( -12, 12 ): + for j in xrange( -12, 12 ): + for k in xrange( -12, 12 ): + self._list_op_test( initial_value, make_op_func( i, j, k ) ) + + + def test_len(self): + jlist = ArrayList() + jlist.addAll( range(0, 10) ) + + self.assert_( len( jlist ) == 10 ) + + + def test_iter(self): + jlist = ArrayList() + jlist.addAll( range(0, 10) ) + + i = iter( jlist ) + + x = list( i ) + + self.assert_( x == range(0, 10) ) + + + + def test_override_len(self): + class MyList (ArrayList): + def __len__(self): + return self.size() + 1; + + + m = MyList() + m.addAll( range(0,10) ) + + self.assert_( len( m ) == 11 ) + + + def test_override_iter(self): + class MyList (ArrayList): + def __iter__(self): + return iter( self.subList( 0, self.size() - 1 ) ); + + + m = MyList() + m.addAll( range(0,10) ) + i = iter( m ) + x = list( i ) + + self.assert_( x == range( 0, 9 ) ) + + + def test_override_getsetdelitem(self): + # Create an ArrayList subclass that provides some silly overrides for get/set/del item + class MyList (ArrayList): + def __getitem__(self, key): + return self.get( key ) * 2; + + def __setitem__(self, key, value): + return self.set( key, value * 2 ); + + def __delitem__(self, key): + self.add( 84 ) + + + m = MyList() + m.addAll( range(0,10) ) + + self.assert_( m[1] == 2 ) + self.assert_( m.get( 1 ) == 1 ) + + m[0] = 3 + self.assert_( m.get( 0 ) == 6 ) + self.assert_( m[0] == 12 ) + + del m[0] + self.assert_( m.size() == 11 ) + self.assert_( m.get( 10 ) == 84 ) + + + + + + +def test_main(): + test.test_support.run_unittest(CollectionProxyTest) + +if __name__ == "__main__": + test_main()