View Javadoc

1   package org.apache.commons.ognl;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.*;
23  
24  /**
25   * Implementation of PropertyAccessor that uses numbers and dynamic subscripts as properties to index into Lists.
26   *
27   * @author Luke Blanshard (blanshlu@netscape.net)
28   * @author Drew Davidson (drew@ognl.org)
29   */
30  public class ListPropertyAccessor
31      extends ObjectPropertyAccessor
32      implements PropertyAccessor
33  {
34  
35      @Override
36      public Object getProperty( Map<String, Object> context, Object target, Object name )
37          throws OgnlException
38      {
39          List<?> list = (List<?>) target;
40  
41          if ( name instanceof String )
42          {
43              Object result;
44  
45              if ( "size".equals( name ) )
46              {
47                  result = list.size();
48              }
49              else
50              {
51                  if ( "iterator".equals( name ) )
52                  {
53                      result = list.iterator();
54                  }
55                  else
56                  {
57                      if ( "isEmpty".equals( name ) || "empty".equals( name ) )
58                      {
59                          result = list.isEmpty() ? Boolean.TRUE : Boolean.FALSE;
60                      }
61                      else
62                      {
63                          result = super.getProperty( context, target, name );
64                      }
65                  }
66              }
67  
68              return result;
69          }
70  
71          if ( name instanceof Number )
72          {
73              return list.get( ( (Number) name ).intValue() );
74          }
75  
76          if ( name instanceof DynamicSubscript )
77          {
78              int len = list.size();
79              switch ( ( (DynamicSubscript) name ).getFlag() )
80              {
81                  case DynamicSubscript.FIRST:
82                      return len > 0 ? list.get( 0 ) : null;
83                  case DynamicSubscript.MID:
84                      return len > 0 ? list.get( len / 2 ) : null;
85                  case DynamicSubscript.LAST:
86                      return len > 0 ? list.get( len - 1 ) : null;
87                  case DynamicSubscript.ALL:
88                      return new ArrayList<Object>( list );
89                  default:
90                      break;
91              }
92          }
93  
94          throw new NoSuchPropertyException( target, name );
95      }
96  
97      @Override
98      public void setProperty( Map<String, Object> context, Object target, Object name, Object value )
99          throws OgnlException
100     {
101         if ( name instanceof String && !( (String) name ).contains( "$" ) )
102         {
103             super.setProperty( context, target, name, value );
104             return;
105         }
106 
107         @SuppressWarnings( "unchecked" ) // check performed by the invoker
108             List<Object> list = (List<Object>) target;
109 
110         if ( name instanceof Number )
111         {
112             list.set( ( (Number) name ).intValue(), value );
113             return;
114         }
115 
116         if ( name instanceof DynamicSubscript )
117         {
118             int len = list.size();
119             switch ( ( (DynamicSubscript) name ).getFlag() )
120             {
121                 case DynamicSubscript.FIRST:
122                     if ( len > 0 )
123                     {
124                         list.set( 0, value );
125                     }
126                     return;
127                 case DynamicSubscript.MID:
128                     if ( len > 0 )
129                     {
130                         list.set( len / 2, value );
131                     }
132                     return;
133                 case DynamicSubscript.LAST:
134                     if ( len > 0 )
135                     {
136                         list.set( len - 1, value );
137                     }
138                     return;
139                 case DynamicSubscript.ALL:
140                     if ( !( value instanceof Collection ) )
141                     {
142                         throw new OgnlException( "Value must be a collection" );
143                     }
144                     list.clear();
145                     list.addAll( (Collection<?>) value );
146                     return;
147                 default:
148                     return;
149             }
150         }
151 
152         throw new NoSuchPropertyException( target, name );
153     }
154 
155     @Override
156     public Class<?> getPropertyClass( OgnlContext context, Object target, Object index )
157     {
158         if ( index instanceof String )
159         {
160             String key = ( (String) index ).replaceAll( "\"", "" );
161             if ( "size".equals( key ) )
162             {
163                 return int.class;
164             }
165             if ( "iterator".equals( key ) )
166             {
167                 return Iterator.class;
168             }
169             if ( "isEmpty".equals( key ) || "empty".equals( key ) )
170             {
171                 return boolean.class;
172             }
173             return super.getPropertyClass( context, target, index );
174         }
175 
176         if ( index instanceof Number )
177         {
178             return Object.class;
179         }
180 
181         return null;
182     }
183 
184     @Override
185     public String getSourceAccessor( OgnlContext context, Object target, Object index )
186     {
187         String indexStr = index.toString().replaceAll( "\"", "" );
188 
189         if ( String.class.isInstance( index ) )
190         {
191             if ( "size".equals( indexStr ) )
192             {
193                 context.setCurrentAccessor( List.class );
194                 context.setCurrentType( int.class );
195                 return ".size()";
196             }
197             if ( "iterator".equals( indexStr ) )
198             {
199                 context.setCurrentAccessor( List.class );
200                 context.setCurrentType( Iterator.class );
201                 return ".iterator()";
202             }
203             if ( "isEmpty".equals( indexStr ) || "empty".equals( indexStr ) )
204             {
205                 context.setCurrentAccessor( List.class );
206                 context.setCurrentType( boolean.class );
207                 return ".isEmpty()";
208             }
209         }
210 
211         // TODO: This feels really inefficient, must be some better way
212         // check if the index string represents a method on a custom class implementing java.util.List instead..
213         return getSourceBeanMethod( context, target, index, indexStr, false );
214     }
215 
216     @Override
217     public String getSourceSetter( OgnlContext context, Object target, Object index )
218     {
219         String indexStr = index.toString().replaceAll( "\"", "" );
220 
221         // TODO: This feels really inefficient, must be some better way
222         // check if the index string represents a method on a custom class implementing java.util.List instead..
223         /*
224          * System.out.println("Listpropertyaccessor setter using index: " + index + " and current object: " +
225          * context.getCurrentObject() + " number is current object? " +
226          * Number.class.isInstance(context.getCurrentObject()));
227          */
228 
229         return getSourceBeanMethod( context, target, index, indexStr, true );
230     }
231 
232     private String getSourceBeanMethod( OgnlContext context, Object target, Object index, String indexStr,
233                                         boolean isSetter )
234     {
235         Object currentObject = context.getCurrentObject();
236         Class<?> currentType = context.getCurrentType();
237         if ( currentObject != null && !Number.class.isInstance( currentObject ) )
238         {
239             try
240             {
241                 if ( isSetter )
242                 {
243                     if ( OgnlRuntime.getWriteMethod( target.getClass(), indexStr ) != null
244                         || !currentType.isPrimitive() )
245                     {
246                         return super.getSourceSetter( context, target, index );
247                     }
248                 }
249                 else
250                 {
251                     if ( OgnlRuntime.getReadMethod( target.getClass(), indexStr ) != null )
252                     {
253                         return super.getSourceAccessor( context, target, index );
254                     }
255                 }
256             }
257             catch ( Throwable t )
258             {
259                 throw OgnlOps.castToRuntime( t );
260             }
261         }
262 
263         /*
264          * if (String.class.isInstance(index)) { context.setCurrentAccessor(List.class); return ""; }
265          */
266 
267         context.setCurrentAccessor( List.class );
268 
269         // need to convert to primitive for list index access
270 
271         if ( !currentType.isPrimitive() && Number.class.isAssignableFrom( currentType ) )
272         {
273             indexStr += "." + OgnlRuntime.getNumericValueGetter( currentType );
274         }
275         else if ( currentObject != null && Number.class.isAssignableFrom( currentObject.getClass() )
276             && !currentType.isPrimitive() )
277         {
278             // means it needs to be cast first as well
279 
280             String toString = String.class.isInstance( index ) && currentType != Object.class ? "" : ".toString()";
281 
282             indexStr = "org.apache.commons.ognl.OgnlOps#getIntValue(" + indexStr + toString + ")";
283         }
284 
285         context.setCurrentType( Object.class );
286 
287         return isSetter ? ".set(" + indexStr + ", $3)" : ".get(" + indexStr + ")";
288     }
289 
290 }