ArrayELResolver.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package jakarta.el;

  18. import java.beans.FeatureDescriptor;
  19. import java.lang.reflect.Array;
  20. import java.util.Iterator;
  21. import java.util.Objects;

  22. /**
  23.  * Standard ELResolver for working with arrays.
  24.  */
  25. public class ArrayELResolver extends ELResolver {

  26.     private final boolean readOnly;

  27.     /**
  28.      * Creates a writable instance of the standard array resolver.
  29.      */
  30.     public ArrayELResolver() {
  31.         this.readOnly = false;
  32.     }

  33.     /**
  34.      * Creates an instance of the standard array resolver.
  35.      *
  36.      * @param readOnly {@code true} if the created instance should be read-only otherwise false.
  37.      */
  38.     public ArrayELResolver(boolean readOnly) {
  39.         this.readOnly = readOnly;
  40.     }

  41.     @Override
  42.     public Class<?> getType(ELContext context, Object base, Object property) {
  43.         Objects.requireNonNull(context);

  44.         if (base != null && base.getClass().isArray()) {
  45.             context.setPropertyResolved(base, property);
  46.             try {
  47.                 int idx = coerce(property);
  48.                 checkBounds(base, idx);
  49.             } catch (IllegalArgumentException e) {
  50.                 // ignore
  51.             }
  52.             /*
  53.              * The resolver may have been created in read-only mode but the array and its elements will always be
  54.              * read-write.
  55.              */
  56.             if (readOnly) {
  57.                 return null;
  58.             }
  59.             return base.getClass().getComponentType();
  60.         }

  61.         return null;
  62.     }

  63.     @Override
  64.     public Object getValue(ELContext context, Object base, Object property) {
  65.         Objects.requireNonNull(context);

  66.         if (base != null && base.getClass().isArray()) {
  67.             context.setPropertyResolved(base, property);
  68.             int idx = coerce(property);
  69.             if (idx < 0 || idx >= Array.getLength(base)) {
  70.                 return null;
  71.             }
  72.             return Array.get(base, idx);
  73.         }

  74.         return null;
  75.     }

  76.     @Override
  77.     public void setValue(ELContext context, Object base, Object property, Object value) {
  78.         Objects.requireNonNull(context);

  79.         if (base != null && base.getClass().isArray()) {
  80.             context.setPropertyResolved(base, property);

  81.             if (this.readOnly) {
  82.                 throw new PropertyNotWritableException(
  83.                         Util.message(context, "resolverNotWritable", base.getClass().getName()));
  84.             }

  85.             int idx = coerce(property);
  86.             checkBounds(base, idx);
  87.             if (value != null && !Util.isAssignableFrom(value.getClass(), base.getClass().getComponentType())) {
  88.                 throw new ClassCastException(Util.message(context, "objectNotAssignable", value.getClass().getName(),
  89.                         base.getClass().getComponentType().getName()));
  90.             }
  91.             Array.set(base, idx, value);
  92.         }
  93.     }

  94.     @Override
  95.     public boolean isReadOnly(ELContext context, Object base, Object property) {
  96.         Objects.requireNonNull(context);

  97.         if (base != null && base.getClass().isArray()) {
  98.             context.setPropertyResolved(base, property);
  99.             try {
  100.                 int idx = coerce(property);
  101.                 checkBounds(base, idx);
  102.             } catch (IllegalArgumentException e) {
  103.                 // ignore
  104.             }
  105.         }

  106.         return this.readOnly;
  107.     }

  108.     @Deprecated(forRemoval = true, since = "EL 5.0")
  109.     @Override
  110.     public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
  111.         return null;
  112.     }

  113.     @Override
  114.     public Class<?> getCommonPropertyType(ELContext context, Object base) {
  115.         if (base != null && base.getClass().isArray()) {
  116.             return Integer.class;
  117.         }
  118.         return null;
  119.     }

  120.     private static void checkBounds(Object base, int idx) {
  121.         if (idx < 0 || idx >= Array.getLength(base)) {
  122.             throw new PropertyNotFoundException(new ArrayIndexOutOfBoundsException(idx).getMessage());
  123.         }
  124.     }

  125.     private static int coerce(Object property) {
  126.         if (property instanceof Number) {
  127.             return ((Number) property).intValue();
  128.         }
  129.         if (property instanceof Character) {
  130.             return ((Character) property).charValue();
  131.         }
  132.         if (property instanceof Boolean) {
  133.             return ((Boolean) property).booleanValue() ? 1 : 0;
  134.         }
  135.         if (property instanceof String) {
  136.             return Integer.parseInt((String) property);
  137.         }
  138.         throw new IllegalArgumentException(property != null ? property.toString() : "null");
  139.     }

  140. }