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.util.*; 023 024 /** 025 * Implementation of PropertyAccessor that uses numbers and dynamic subscripts as properties to index into Lists. 026 * 027 * @author Luke Blanshard (blanshlu@netscape.net) 028 * @author Drew Davidson (drew@ognl.org) 029 */ 030 public class ListPropertyAccessor 031 extends ObjectPropertyAccessor 032 implements PropertyAccessor 033 { 034 035 @Override 036 public Object getProperty( Map<String, Object> context, Object target, Object name ) 037 throws OgnlException 038 { 039 List<?> list = (List<?>) target; 040 041 if ( name instanceof String ) 042 { 043 Object result; 044 045 if ( "size".equals( name ) ) 046 { 047 result = list.size(); 048 } 049 else 050 { 051 if ( "iterator".equals( name ) ) 052 { 053 result = list.iterator(); 054 } 055 else 056 { 057 if ( "isEmpty".equals( name ) || "empty".equals( name ) ) 058 { 059 result = list.isEmpty() ? Boolean.TRUE : Boolean.FALSE; 060 } 061 else 062 { 063 result = super.getProperty( context, target, name ); 064 } 065 } 066 } 067 068 return result; 069 } 070 071 if ( name instanceof Number ) 072 { 073 return list.get( ( (Number) name ).intValue() ); 074 } 075 076 if ( name instanceof DynamicSubscript ) 077 { 078 int len = list.size(); 079 switch ( ( (DynamicSubscript) name ).getFlag() ) 080 { 081 case DynamicSubscript.FIRST: 082 return len > 0 ? list.get( 0 ) : null; 083 case DynamicSubscript.MID: 084 return len > 0 ? list.get( len / 2 ) : null; 085 case DynamicSubscript.LAST: 086 return len > 0 ? list.get( len - 1 ) : null; 087 case DynamicSubscript.ALL: 088 return new ArrayList<Object>( list ); 089 default: 090 break; 091 } 092 } 093 094 throw new NoSuchPropertyException( target, name ); 095 } 096 097 @Override 098 public void setProperty( Map<String, Object> context, Object target, Object name, Object value ) 099 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 }