/* * Copyright (C) 2000 - 2008 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://www.openbluedragon.org/ */ package com.naryx.tagfusion.cfm.engine; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import com.naryx.tagfusion.cfm.tag.cfDUMP; import com.naryx.tagfusion.cfm.tag.tagUtils; /** * This class extends the regular cfArrayData with the purpose of giving * access to java.util.List (and subclasses of) instances. * This will give us the ability to call methods of a subclass of List * that aren't in the declared List methods. * */ public class cfJavaArrayData extends cfArrayData implements List<Object> { static final long serialVersionUID = 1; private List<Object> theList; public cfJavaArrayData( List<Object> _list ){ super(); theList = _list; dimensions = 1; setInstance( _list ); } protected void setSize( int indx ) { while ( theList.size() < indx ){ theList.add( null ); } } public cfData getData( cfData arrayIndex ) throws cfmRunTimeException{ int indx; try{ indx = arrayIndex.getInt(); }catch(Exception E){ cfCatchData catchData = new cfCatchData(); catchData.setType( cfCatchData.TYPE_APPLICATION ); catchData.setMessage( "Attempted to access array with invalid array index." ); throw new cfmRunTimeException( catchData ); } if ( indx > theList.size() ){ setSize(indx); }else if ( indx < 1 ){ cfCatchData catchData = new cfCatchData(); catchData.setType( cfCatchData.TYPE_APPLICATION ); catchData.setMessage( "Attempted to access array with invalid array index [" + indx + "]." ); throw new cfmRunTimeException( catchData ); } return tagUtils.convertToCfData( theList.get( indx-1 ) ); } public void setData( cfData arrayIndex, cfData _data ) throws cfmRunTimeException{ int indx; try{ indx = arrayIndex.getInt(); }catch(Exception E){ cfCatchData catchData = new cfCatchData(); catchData.setType( cfCatchData.TYPE_APPLICATION ); catchData.setMessage( "Attempted to access array with invalid array index." ); throw new cfmRunTimeException( catchData ); } setData( indx, _data ); } public void setData( int _index, cfData _element ){ if ( _index > theList.size() ){ setSize( _index ); } theList.set( ( _index - 1 ), tagUtils.getNatural( _element ) ); } public void addElement( cfData _element ){ theList.add( tagUtils.getNatural( _element ) ); } public void addElementAt( cfData _element, int _index ){ theList.add( ( _index - 1 ), tagUtils.getNatural( _element ) ); } public void elementSwap(int _start, int _end) { Object first = theList.get( ( _start - 1 ) ); Object second = theList.get( ( _end - 1 ) ); theList.set( ( _end - 1 ), first ); theList.set( ( _start - 1 ), second ); } public void removeAllElements() { theList.clear(); } public void removeElementAt(int _no) { theList.remove( _no - 1 ); } public void setElements(int _start, int _end, cfData _value) { for ( int x = _start; x <= _end ; x++ ){ this.setData( x, _value ); } } public void sortArray(String _type, String _order ) throws dataNotSupportedException { String order = ( _order == null ? "asc" : _order ); Comparator<Object> comparator = null; if ( _type.equalsIgnoreCase( "text" ) ){ if ( order.equalsIgnoreCase("asc") ){ comparator = new textComparator( textComparator.ASC, true ); }else{ comparator = new textComparator( textComparator.DESC, true ); } }else if ( _type.equalsIgnoreCase( "textnocase" ) ){ if ( order.equalsIgnoreCase("asc") ){ comparator = new textComparator( textComparator.ASC, true ); }else{ comparator = new textComparator( textComparator.DESC, true ); } Collections.sort( theList, comparator ); }else{ try{ comparator = new numericComparator( order.equalsIgnoreCase("asc") ? numericComparator.ASC : numericComparator.DESC ); Collections.sort( theList, comparator ); }catch( ClassCastException e ){ throw new dataNotSupportedException( "Cannot sort an array that contains non-numeric values numerically." ); } } } public void setCapacity( int _size ){ setSize( _size ); } public cfArrayData copy(){ List<Object> duplicateList = new ArrayList<Object>(); duplicateList.addAll( theList ); return new cfJavaArrayData( duplicateList ); } public cfData duplicate(){ return copy(); } public cfData duplicate( boolean _deepCopy ){ return duplicate(); } public double getMax()throws dataNotSupportedException{ double max; double nextValue; Object nextItem = theList.get(0); if ( nextItem instanceof Number ){ max = ( (Number) nextItem).doubleValue(); }else{ throw new dataNotSupportedException( "Array contains non-numeric." ); } for ( int i = 1; i < theList.size(); i++ ){ nextItem = theList.get(i); if ( nextItem instanceof Number ){ nextValue = ( (Number) nextItem).doubleValue(); if ( nextValue > max ){ max = nextValue; } }else{ throw new dataNotSupportedException( "Array contains non-numeric." ); } } return max; } public double getMin()throws dataNotSupportedException{ double min; double nextValue; Object nextItem = theList.get(0); if ( nextItem instanceof Number ){ min = ( (Number) nextItem).doubleValue(); }else{ throw new dataNotSupportedException( "Array contains non-numeric." ); } for ( int i = 1; i < theList.size(); i++ ){ nextItem = theList.get(i); if ( nextItem instanceof Number ){ nextValue = ( (Number) nextItem).doubleValue(); if ( nextValue < min ){ min = nextValue; } }else{ throw new dataNotSupportedException( "Array contains non-numeric." ); } } return min; } public double getAverage()throws dataNotSupportedException{ return getSum() / size(); } public double getSum()throws dataNotSupportedException{ Object nextItem; double sum = 0.0; for ( int i = 0; i < theList.size(); i++ ){ nextItem = theList.get(i); if ( nextItem instanceof Number ){ sum += ( (Number) nextItem).doubleValue(); }else{ throw new dataNotSupportedException( "Array contains non-numeric." ); } } return sum; } public cfData getElement( int _index ){ return tagUtils.convertToCfData( theList.get( ( _index - 1 ) ) ); } public String toString(){ return "{ARRAY:" + theList + "}"; } public String createList( String delimiter ) { StringBuilder list = new StringBuilder(); Iterator<Object> iter = theList.iterator(); while ( iter.hasNext() ) { Object next = iter.next(); if ( next != null ) { list.append( next.toString() ); } list.append( delimiter ); } return ( theList.size() > 0 ? list.toString().substring( 0, list.length()-1 ) : "" ); } public void dump( java.io.PrintWriter out ){ dump( out, "", cfDUMP.TOP_DEFAULT ); } public void dump( java.io.PrintWriter out, String _label, int _top ){ out.write( "<table class='cfdump_table_array'>" ); if ( theList.size() > 0 ) { out.write( "<th class='cfdump_th_array' colspan='2'>"); if ( _label.length() > 0 ) out.write( _label + " - " ); out.write( "array</th>" ); for ( int x=0; x < theList.size(); x++ ){ out.write( "<tr><td class='cfdump_td_array'>" ); out.write( (x+1) + "" ); out.write( "</td><td class='cfdump_td_value'>" ); cfData element = tagUtils.convertToCfData( theList.get( x ) ); if ( ( element == null ) || ( element.getDataType() == cfData.CFNULLDATA ) ) out.write( "[undefined array element]" ); else element.dump(out); out.write( "</td></tr>" ); } } else { out.write( "<th class='cfdump_th_array' colspan='2'>array [empty]</th>" ); } out.write( "</table>" ); } public void dumpWDDX( int version, java.io.PrintWriter out ){ if ( version > 10 ) out.write( "<a l='" ); else out.write( "<array length='" ); out.write( theList.size() + "" ); out.write( "'>" ); for ( int x=0; x < theList.size(); x++ ){ if( theList.get( x ) != null) (tagUtils.convertToCfData(theList.get( x ))).dumpWDDX( version, out ); } if ( version > 10 ) out.write( "</a>" ); else out.write( "</array>" ); } /******************************************************************************* * The following methods implement the java.util.List interface. They're * implemented to support variable sharing between CFML and servlets/JSP * pages. These methods are just wrappers around the internal "theList" attribute. * *******************************************************************************/ /** * Returns the number of elements in this list. If this list contains * more than <tt>Integer.MAX_VALUE</tt> elements, returns * <tt>Integer.MAX_VALUE</tt>. * * @return the number of elements in this list. */ public int size() { return theList.size(); } /** * Returns <tt>true</tt> if this list contains no elements. * * @return <tt>true</tt> if this list contains no elements. */ public boolean isEmpty() { return theList.isEmpty(); } /** * * Returns <tt>true</tt> if this list contains the specified element. * More formally, returns <tt>true</tt> if and only if this list contains * at least one element <tt>e</tt> such that * <tt>(o==null ? e==null : o.equals(e))</tt>. * * @param o element whose presence in this list is to be tested. * @return <tt>true</tt> if this list contains the specified element. */ public boolean contains( Object o ) { return theList.contains( o ); } /** * Returns an iterator over the elements in this list in proper sequence. * * @return an iterator over the elements in this list in proper sequence. */ public Iterator<Object> iterator() { return theList.iterator(); } /** * Returns a list iterator of the elements in this list (in proper sequence). */ public ListIterator<Object> listIterator() { return theList.listIterator(); } /** * Returns a list iterator of the elements in this list (in proper sequence), * starting at the specified position in this list. */ public ListIterator<Object> listIterator( int index ) { return theList.listIterator( index ); } /** * Returns an array containing all of the elements in this list in proper * sequence. Obeys the general contract of the * <tt>Collection.toArray</tt> method. * * @return an array containing all of the elements in this list in proper * sequence. * @see Arrays#asList(Object[]) */ public Object[] toArray() { return theList.toArray(); } /** * Returns an array containing all of the elements in this list in proper * sequence; the runtime type of the returned array is that of the * specified array. Obeys the general contract of the * <tt>Collection.toArray(Object[])</tt> method. * * @param a the array into which the elements of this list are to * be stored, if it is big enough; otherwise, a new array of the * same runtime type is allocated for this purpose. * @return an array containing the elements of this list. * * @throws ArrayStoreException if the runtime type of the specified array * is not a supertype of the runtime type of every element in * this list. */ @SuppressWarnings("unchecked") public Object[] toArray( Object a[] ) { return theList.toArray( a ); } // Modification Operations /** * Appends the specified element to the end of this list (optional * operation). <p> * * Lists that support this operation may place limitations on what * elements may be added to this list. In particular, some * lists will refuse to add null elements, and others will impose * restrictions on the type of elements that may be added. List * classes should clearly specify in their documentation any restrictions * on what elements may be added. * * @param o element to be appended to this list. * @return <tt>true</tt> (as per the general contract of the * <tt>Collection.add</tt> method). * * @throws UnsupportedOperationException if the <tt>add</tt> method is not * supported by this list. * @throws ClassCastException if the class of the specified element * prevents it from being added to this list. * @throws IllegalArgumentException if some aspect of this element * prevents it from being added to this collection. */ public boolean add( Object o ) { return theList.add( o ); } /** * Removes the first occurrence in this list of the specified element * (optional operation). If this list does not contain the element, it is * unchanged. More formally, removes the element with the lowest index i * such that <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt> (if * such an element exists). * * @param o element to be removed from this list, if present. * @return <tt>true</tt> if this list contained the specified element. * * @throws UnsupportedOperationException if the <tt>remove</tt> method is * not supported by this list. */ public boolean remove( Object o ) { return theList.remove( o ); } // Bulk Modification Operations /** * * Returns <tt>true</tt> if this list contains all of the elements of the * specified collection. * * @param c collection to be checked for containment in this list. * @return <tt>true</tt> if this list contains all of the elements of the * specified collection. * * @see #contains(Object) */ public boolean containsAll( Collection<?> c ) { return theList.containsAll( c ); } /** * Appends all of the elements in the specified collection to the end of * this list, in the order that they are returned by the specified * collection's iterator (optional operation). The behavior of this * operation is unspecified if the specified collection is modified while * the operation is in progress. (Note that this will occur if the * specified collection is this list, and it's nonempty.) * * @param c collection whose elements are to be added to this list. * @return <tt>true</tt> if this list changed as a result of the call. * * @throws UnsupportedOperationException if the <tt>addAll</tt> method is * not supported by this list. * * @throws ClassCastException if the class of an element in the specified * collection prevents it from being added to this list. * * @throws IllegalArgumentException if some aspect of an element in the * specified collection prevents it from being added to this * list. * * @see #add(Object) */ public boolean addAll( Collection<?> c ) { return theList.addAll( c ); } /** * Inserts all of the elements in the specified collection into this * list at the specified position (optional operation). Shifts the * element currently at that position (if any) and any subsequent * elements to the right (increases their indices). The new elements * will appear in this list in the order that they are returned by the * specified collection's iterator. The behavior of this operation is * unspecified if the specified collection is modified while the * operation is in progress. (Note that this will occur if the specified * collection is this list, and it's nonempty.) * * @param index index at which to insert first element from the specified * collection. * @param c elements to be inserted into this list. * @return <tt>true</tt> if this list changed as a result of the call. * * @throws UnsupportedOperationException if the <tt>addAll</tt> method is * not supported by this list. * @throws ClassCastException if the class of one of elements of the * specified collection prevents it from being added to this * list. * @throws IllegalArgumentException if some aspect of one of elements of * the specified collection prevents it from being added to * this list. * @throws IndexOutOfBoundsException if the index is out of range (index * < 0 || index > size()). */ public boolean addAll( int index, Collection<?> c ) { return theList.addAll( index, c ); } /** * Removes from this list all the elements that are contained in the * specified collection (optional operation). * * @param c collection that defines which elements will be removed from * this list. * @return <tt>true</tt> if this list changed as a result of the call. * * @throws UnsupportedOperationException if the <tt>removeAll</tt> method * is not supported by this list. * * @see #remove(Object) * @see #contains(Object) */ public boolean removeAll( Collection<?> c ) { return theList.removeAll( c ); } /** * Retains only the elements in this list that are contained in the * specified collection (optional operation). In other words, removes * from this list all the elements that are not contained in the specified * collection. * * @param c collection that defines which elements this set will retain. * * @return <tt>true</tt> if this list changed as a result of the call. * * @throws UnsupportedOperationException if the <tt>retainAll</tt> method * is not supported by this list. * * @see #remove(Object) * @see #contains(Object) */ public boolean retainAll( Collection<?> c ) { return theList.retainAll( c ); } /** * Removes all of the elements from this list (optional operation). This * list will be empty after this call returns (unless it throws an * exception). * * @throws UnsupportedOperationException if the <tt>clear</tt> method is * not supported by this list. */ public void clear() { theList.clear(); } // Comparison and hashing /** * Compares the specified object with this list for equality. Returns * <tt>true</tt> if and only if the specified object is also a list, both * lists have the same size, and all corresponding pairs of elements in * the two lists are <i>equal</i>. (Two elements <tt>e1</tt> and * <tt>e2</tt> are <i>equal</i> if <tt>(e1==null ? e2==null : * e1.equals(e2))</tt>.) In other words, two lists are defined to be * equal if they contain the same elements in the same order. This * definition ensures that the equals method works properly across * different implementations of the <tt>List</tt> interface. * * @param o the object to be compared for equality with this list. * @return <tt>true</tt> if the specified object is equal to this list. */ public boolean equals( Object o ) { return theList.equals( o ); } /** * Returns the hash code value for this list. The hash code of a list * is defined to be the result of the following calculation: * <pre> * hashCode = 1; * Iterator i = list.iterator(); * while (i.hasNext()) { * Object obj = i.next(); * hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode()); * } * </pre> * This ensures that <tt>list1.equals(list2)</tt> implies that * <tt>list1.hashCode()==list2.hashCode()</tt> for any two lists, * <tt>list1</tt> and <tt>list2</tt>, as required by the general * contract of <tt>Object.hashCode</tt>. * * @return the hash code value for this list. * @see Object#hashCode() * @see Object#equals(Object) * @see #equals(Object) */ public int hashCode() { return theList.hashCode(); } // Positional Access Operations /** * Returns the element at the specified position in this list. * * @param index index of element to return. * @return the element at the specified position in this list. * * @throws IndexOutOfBoundsException if the index is out of range (index * < 0 || index >= size()). */ public Object get( int index ) { return theList.get( index ); } /** * Replaces the element at the specified position in this list with the * specified element (optional operation). * * @param index index of element to replace. * @param element element to be stored at the specified position. * @return the element previously at the specified position. * * @throws UnsupportedOperationException if the <tt>set</tt> method is not * supported by this list. * @throws ClassCastException if the class of the specified element * prevents it from being added to this list. * @throws IllegalArgumentException if some aspect of the specified * element prevents it from being added to this list. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index >= size()). */ public Object set( int index, Object element ) { return theList.set( index, element ); } /** * Inserts the specified element at the specified position in this list * (optional operation). Shifts the element currently at that position * (if any) and any subsequent elements to the right (adds one to their * indices). * * @param index index at which the specified element is to be inserted. * @param element element to be inserted. * * @throws UnsupportedOperationException if the <tt>add</tt> method is not * supported by this list. * @throws ClassCastException if the class of the specified element * prevents it from being added to this list. * @throws IllegalArgumentException if some aspect of the specified * element prevents it from being added to this list. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index > size()). */ public void add( int index, Object element ) { theList.add( index, element ); } /** * Removes the element at the specified position in this list (optional * operation). Shifts any subsequent elements to the left (subtracts one * from their indices). Returns the element that was removed from the * list. * * @param index the index of the element to removed. * @return the element previously at the specified position. * * @throws UnsupportedOperationException if the <tt>remove</tt> method is * not supported by this list. * * @throws IndexOutOfBoundsException if the index is out of range (index * < 0 || index >= size()). */ public Object remove( int index ) { return theList.remove( index ); } // Search Operations /** * Returns the index in this list of the first occurrence of the specified * element, or -1 if this list does not contain this element. * More formally, returns the lowest index <tt>i</tt> such that * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>, * or -1 if there is no such index. * * @param o element to search for. * @return the index in this list of the first occurrence of the specified * element, or -1 if this list does not contain this element. */ public int indexOf( Object o ) { return theList.indexOf( o ); } /** * Returns the index in this list of the last occurrence of the specified * element, or -1 if this list does not contain this element. * More formally, returns the highest index <tt>i</tt> such that * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>, * or -1 if there is no such index. * * @param o element to search for. * @return the index in this list of the last occurrence of the specified * element, or -1 if this list does not contain this element. */ public int lastIndexOf( Object o ) { return theList.lastIndexOf( o ); } // View /** * Returns a view of the portion of this list between the specified * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive. (If * <tt>fromIndex</tt> and <tt>toIndex</tt> are equal, the returned list is * empty.) The returned list is backed by this list, so changes in the * returned list are reflected in this list, and vice-versa. The returned * list supports all of the optional list operations supported by this * list.<p> * * This method eliminates the need for explicit range operations (of * the sort that commonly exist for arrays). Any operation that expects * a list can be used as a range operation by passing a subList view * instead of a whole list. For example, the following idiom * removes a range of elements from a list: * <pre> * list.subList(from, to).clear(); * </pre> * Similar idioms may be constructed for <tt>indexOf</tt> and * <tt>lastIndexOf</tt>, and all of the algorithms in the * <tt>Collections</tt> class can be applied to a subList.<p> * * The semantics of this list returned by this method become undefined if * the backing list (i.e., this list) is <i>structurally modified</i> in * any way other than via the returned list. (Structural modifications are * those that change the size of this list, or otherwise perturb it in such * a fashion that iterations in progress may yield incorrect results.) * * @param fromIndex low endpoint (inclusive) of the subList. * @param toIndex high endpoint (exclusive) of the subList. * @return a view of the specified range within this list. * * @throws IndexOutOfBoundsException for an illegal endpoint index value * (fromIndex < 0 || toIndex > size || fromIndex > toIndex). */ public List<Object> subList( int fromIndex, int toIndex ) { return theList.subList( fromIndex, toIndex ); } /** * Comparator implementations for sortArray() */ class textComparator implements Comparator<Object> { public static final byte ASC=0, DESC=1; private byte type; private boolean casesensitive; public textComparator( byte _type, boolean _case ){ type = _type; casesensitive = _case; } public int compare( Object _o1, Object _o2 ){ int result; if ( casesensitive ){ result = _o1.toString().compareTo( _o2.toString() ); }else{ result = _o1.toString().compareToIgnoreCase( _o2.toString() ); } if ( type == ASC ){ return result; }else{ return -1 * result; } } } class numericComparator implements Comparator<Object> { public static final byte ASC=0, DESC=1; private byte type; public numericComparator( byte _type ){ type = _type; } public int compare( Object _o1, Object _o2 ){ int result; if ( !(_o1 instanceof Number && _o2 instanceof Number )) throw new ClassCastException(); if ( _o1.equals( _o2 ) ){ result = 0; }else if ( ((Number) _o1).floatValue() < ((Number) _o2).floatValue() ){ result = -1; }else{ result = 1; } if ( type == ASC ){ return result; }else{ return -1 * result; } } } }