001    package org.apache.commons.ognl;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.lang.reflect.Array;
023    import java.util.Map;
024    
025    /**
026     * Implementation of PropertyAccessor that uses numbers and dynamic subscripts as properties to index into Java arrays.
027     * 
028     * @author Luke Blanshard (blanshlu@netscape.net)
029     * @author Drew Davidson (drew@ognl.org)
030     */
031    public class ArrayPropertyAccessor
032        extends ObjectPropertyAccessor
033        implements PropertyAccessor
034    {
035    
036        @Override
037        public Object getProperty( Map<String, Object> context, Object target, Object name )
038            throws OgnlException
039        {
040            Object result = null;
041    
042            if ( name instanceof String )
043            {
044                if ( "length".equals( name ) )
045                {
046                    result = Array.getLength( target );
047                }
048                else
049                {
050                    result = super.getProperty( context, target, name );
051                }
052            }
053            else
054            {
055                Object index = name;
056    
057                if ( index instanceof DynamicSubscript )
058                {
059                    int len = Array.getLength( target );
060    
061                    switch ( ( (DynamicSubscript) index ).getFlag() )
062                    {
063                        case DynamicSubscript.ALL:
064                            result = Array.newInstance( target.getClass().getComponentType(), len );
065                            System.arraycopy( target, 0, result, 0, len );
066                            break;
067                        case DynamicSubscript.FIRST:
068                            index = ( len > 0 ) ? 0 : -1;
069                            break;
070                        case DynamicSubscript.MID:
071                            index = ( len > 0 ) ? ( len / 2 ) : -1;
072                            break;
073                        case DynamicSubscript.LAST:
074                            index = ( len > 0 ) ? ( len - 1 ) : -1;
075                            break;
076                        default: break;
077                    }
078                }
079                if ( result == null )
080                {
081                    if ( index instanceof Number )
082                    {
083                        int i = ( (Number) index ).intValue();
084    
085                        result = ( i >= 0 ) ? Array.get( target, i ) : null;
086                    }
087                    else
088                    {
089                        throw new NoSuchPropertyException( target, index );
090                    }
091                }
092            }
093            return result;
094        }
095    
096        @Override
097        public void setProperty( Map<String, Object> context, Object target, Object name, Object value )
098            throws OgnlException
099        {
100            boolean isNumber = ( name instanceof Number );
101    
102            if ( isNumber || ( name instanceof DynamicSubscript ) )
103            {
104                TypeConverter converter = ( (OgnlContext) context ).getTypeConverter();
105                Object convertedValue;
106    
107                convertedValue = converter.convertValue( context, target, null, name.toString(), value,
108                                                         target.getClass().getComponentType() );
109                if ( isNumber )
110                {
111                    int i = ( (Number) name ).intValue();
112    
113                    if ( i >= 0 )
114                    {
115                        Array.set( target, i, convertedValue );
116                    }
117                }
118                else
119                {
120                    int len = Array.getLength( target );
121    
122                    switch ( ( (DynamicSubscript) name ).getFlag() )
123                    {
124                        case DynamicSubscript.ALL:
125                            System.arraycopy( target, 0, convertedValue, 0, len );
126                            return;
127                        default:
128                            break;
129                    }
130                }
131            }
132            else
133            {
134                if ( name instanceof String )
135                {
136                    super.setProperty( context, target, name, value );
137                }
138                else
139                {
140                    throw new NoSuchPropertyException( target, name );
141                }
142            }
143        }
144    
145        @Override
146        public String getSourceAccessor( OgnlContext context, Object target, Object index )
147        {
148            String indexStr = getIndexString( context, index );
149    
150            context.setCurrentAccessor( target.getClass() );
151            context.setCurrentType( target.getClass().getComponentType() );
152    
153            return "[" + indexStr + "]";
154        }
155    
156        @Override
157        public String getSourceSetter( OgnlContext context, Object target, Object index )
158        {
159            String indexStr = getIndexString( context, index );
160    
161            Class<?> type = target.getClass().isArray() ? target.getClass().getComponentType() : target.getClass();
162    
163            context.setCurrentAccessor( target.getClass() );
164            context.setCurrentType( target.getClass().getComponentType() );
165    
166            if ( type.isPrimitive() )
167            {
168                Class<?> wrapClass = OgnlRuntime.getPrimitiveWrapperClass( type );
169    
170                return "[" + indexStr + "]=((" + wrapClass.getName() + ")org.apache.commons.ognl.OgnlOps.convertValue($3,"
171                    + wrapClass.getName() + ".class, true))." + OgnlRuntime.getNumericValueGetter( wrapClass );
172            }
173            return "[" + indexStr + "]=org.apache.commons.ognl.OgnlOps.convertValue($3," + type.getName() + ".class)";
174        }
175    
176        private static String getIndexString( OgnlContext context, Object index )
177        {
178            String indexStr = index.toString();
179    
180            // need to convert to primitive for list index access
181    
182            // System.out.println("index class " + index.getClass() + " current type " + context.getCurrentType() +
183            // " current object class " + context.getCurrentObject().getClass());
184    
185            if ( context.getCurrentType() != null && !context.getCurrentType().isPrimitive()
186                && Number.class.isAssignableFrom( context.getCurrentType() ) )
187            {
188                indexStr += "." + OgnlRuntime.getNumericValueGetter( context.getCurrentType() );
189            }
190            else if ( context.getCurrentObject() != null && Number.class.isAssignableFrom(
191                context.getCurrentObject().getClass() ) && !context.getCurrentType().isPrimitive() )
192            {
193                // means it needs to be cast first as well
194    
195                String toString =
196                    String.class.isInstance( index ) && context.getCurrentType() != Object.class ? "" : ".toString()";
197    
198                indexStr = "org.apache.commons.ognl.OgnlOps#getIntValue(" + indexStr + toString + ")";
199            }
200            return indexStr;
201        }
202    }