All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jakarta.el.MapELResolver Maven / Gradle / Ivy

/*
 * Copyright (c) 1997, 2021 Oracle and/or its affiliates and others.
 * All rights reserved.
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jakarta.el;

import static java.lang.Boolean.TRUE;

import java.beans.FeatureDescriptor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Defines property resolution behavior on instances of {@link java.util.Map}.
 *
 * 

* This resolver handles base objects of type java.util.Map. It accepts any object as a property and uses * that object as a key in the map. The resulting value is the value in the map that is associated with that key. *

* *

* This resolver can be constructed in read-only mode, which means that {@link #isReadOnly} will always return * true and {@link #setValue} will always throw PropertyNotWritableException. *

* *

* ELResolvers are combined together using {@link CompositeELResolver}s, to define rich semantics for * evaluating an expression. See the javadocs for {@link ELResolver} for details. *

* * @see CompositeELResolver * @see ELResolver * @see java.util.Map * @since Jakarta Server Pages 2.1 */ public class MapELResolver extends ELResolver { static private Class theUnmodifiableMapClass = Collections.unmodifiableMap(new HashMap<>()).getClass(); private boolean isReadOnly; /** * Creates a new read/write MapELResolver. */ public MapELResolver() { isReadOnly = false; } /** * Creates a new MapELResolver whose read-only status is determined by the given parameter. * * @param isReadOnly true if this resolver cannot modify maps; false otherwise. */ public MapELResolver(boolean isReadOnly) { this.isReadOnly = isReadOnly; } /** * If the base object is a map, returns the most general acceptable type for a value in this map. * *

* If the base is a Map, the propertyResolved property of the ELContext object * must be set to true by this resolver, before returning. If this property is not true after * this method is called, the caller should ignore the return value. *

* *

* Assuming the base is a Map, this method will always return Object.class unless the * resolver is constructed in read-only mode in which case {@code null} will be returned. This is because * Maps accept any object as the value for a given key. *

* * @param context The context of this evaluation. * @param base The map to analyze. Only bases of type Map are handled by this resolver. * @param property The key to return the acceptable type for. Ignored by this resolver. * @return If the propertyResolved property of ELContext was set to true, then * the most general acceptable type which must be {@code null} if the either the property or the resolver is * read-only; otherwise undefined * @throws NullPointerException if context is null * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown * exception must be included as the cause property of this exception, if available. */ @Override public Class getType(ELContext context, Object base, Object property) { if (context == null) { throw new NullPointerException(); } if (base != null && base instanceof Map) { context.setPropertyResolved(true); Map map = (Map) base; if (isReadOnly || map.getClass() == theUnmodifiableMapClass) { return null; } return Object.class; } return null; } /** * If the base object is a map, returns the value associated with the given key, as specified by the * property argument. If the key was not found, null is returned. * *

* If the base is a Map, the propertyResolved property of the ELContext object * must be set to true by this resolver, before returning. If this property is not true after * this method is called, the caller should ignore the return value. *

* *

* Just as in {@link java.util.Map#get}, just because null is returned doesn't mean there is no mapping for * the key; it's also possible that the Map explicitly maps the key to null. *

* * @param context The context of this evaluation. * @param base The map to be analyzed. Only bases of type Map are handled by this resolver. * @param property The key whose associated value is to be returned. * @return If the propertyResolved property of ELContext was set to true, then * the value associated with the given key or null if the key was not found. Otherwise, undefined. * @throws ClassCastException if the key is of an inappropriate type for this map (optionally thrown by the underlying * Map). * @throws NullPointerException if context is null, or if the key is null and this map does not permit null * keys (the latter is optionally thrown by the underlying Map). * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown * exception must be included as the cause property of this exception, if available. */ @Override public Object getValue(ELContext context, Object base, Object property) { if (context == null) { throw new NullPointerException(); } if (base != null && base instanceof Map) { context.setPropertyResolved(base, property); Map map = (Map) base; return map.get(property); } return null; } /** * If the base object is a map, attempts to set the value associated with the given key, as specified by the * property argument. * *

* If the base is a Map, the propertyResolved property of the ELContext object * must be set to true by this resolver, before returning. If this property is not true after * this method is called, the caller can safely assume no value was set. *

* *

* If this resolver was constructed in read-only mode, this method will always throw * PropertyNotWritableException. *

* *

* If a Map was created using {@link java.util.Collections#unmodifiableMap}, this method must throw * PropertyNotWritableException. Unfortunately, there is no Collections API method to detect this. However, * an implementation can create a prototype unmodifiable Map and query its runtime type to see if it * matches the runtime type of the base object as a workaround. *

* * @param context The context of this evaluation. * @param base The map to be modified. Only bases of type Map are handled by this resolver. * @param property The key with which the specified value is to be associated. * @param val The value to be associated with the specified key. * @throws ClassCastException if the class of the specified key or value prevents it from being stored in this map. * @throws NullPointerException if context is null, or if this map does not permit null keys * or values, and the specified key or value is null. * @throws IllegalArgumentException if some aspect of this key or value prevents it from being stored in this map. * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown * exception must be included as the cause property of this exception, if available. * @throws PropertyNotWritableException if this resolver was constructed in read-only mode, or if the put operation is * not supported by the underlying map. */ @Override public void setValue(ELContext context, Object base, Object property, Object val) { if (context == null) { throw new NullPointerException(); } if (base != null && base instanceof Map) { context.setPropertyResolved(base, property); // The cast is safe @SuppressWarnings("unchecked") Map map = (Map) base; if (isReadOnly || map.getClass() == theUnmodifiableMapClass) { throw new PropertyNotWritableException(); } try { map.put(property, val); } catch (UnsupportedOperationException ex) { throw new PropertyNotWritableException(); } } } /** * If the base object is a map, returns whether a call to {@link #setValue} will always fail. * *

* If the base is a Map, the propertyResolved property of the ELContext object * must be set to true by this resolver, before returning. If this property is not true after * this method is called, the caller should ignore the return value. *

* *

* If this resolver was constructed in read-only mode, this method will always return true. *

* *

* If a Map was created using {@link java.util.Collections#unmodifiableMap}, this method must return * true. Unfortunately, there is no Collections API method to detect this. However, an implementation can * create a prototype unmodifiable Map and query its runtime type to see if it matches the runtime type of * the base object as a workaround. *

* * @param context The context of this evaluation. * @param base The map to analyze. Only bases of type Map are handled by this resolver. * @param property The key to return the read-only status for. Ignored by this resolver. * @return If the propertyResolved property of ELContext was set to true, then * true if calling the setValue method will always fail or false if it is * possible that such a call may succeed; otherwise undefined. * @throws NullPointerException if context is null * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown * exception must be included as the cause property of this exception, if available. */ @Override public boolean isReadOnly(ELContext context, Object base, Object property) { if (context == null) { throw new NullPointerException(); } if (base != null && base instanceof Map) { context.setPropertyResolved(true); Map map = (Map) base; return isReadOnly || map.getClass() == theUnmodifiableMapClass; } return false; } /** * If the base object is a map, returns an Iterator containing the set of keys available in the * Map. Otherwise, returns null. * *

* The Iterator returned must contain zero or more instances of {@link java.beans.FeatureDescriptor}. Each * info object contains information about a key in the Map, and is initialized as follows: *

    *
  • displayName - The return value of calling the toString method on this key, or "null" if * the key is null.
  • *
  • name - Same as displayName property.
  • *
  • shortDescription - Empty string
  • *
  • expert - false
  • *
  • hidden - false
  • *
  • preferred - true
  • *
* * In addition, the following named attributes must be set in the returned FeatureDescriptors: *
    *
  • {@link ELResolver#TYPE} - The return value of calling the getClass() method on this key, or * null if the key is null.
  • *
  • {@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - true
  • *
* * * @param context The context of this evaluation. * @param base The map whose keys are to be iterated over. Only bases of type Map are handled by this * resolver. * @return An Iterator containing zero or more (possibly infinitely more) FeatureDescriptor * objects, each representing a key in this map, or null if the base object is not a map. * * @deprecated This method will be removed without replacement in EL 6.0 */ @Deprecated(forRemoval = true, since = "5.0") @Override public Iterator getFeatureDescriptors(ELContext context, Object base) { if (base != null && base instanceof Map) { Map map = (Map) base; Iterator iter = map.keySet().iterator(); List list = new ArrayList<>(); while (iter.hasNext()) { Object key = iter.next(); FeatureDescriptor descriptor = new FeatureDescriptor(); String name = key == null ? null : key.toString(); descriptor.setName(name); descriptor.setDisplayName(name); descriptor.setShortDescription(""); descriptor.setExpert(false); descriptor.setHidden(false); descriptor.setPreferred(true); if (key != null) { descriptor.setValue("type", key.getClass()); } descriptor.setValue("resolvableAtDesignTime", TRUE); list.add(descriptor); } return list.iterator(); } return null; } /** * If the base object is a map, returns the most general type that this resolver accepts for the property * argument. Otherwise, returns null. * *

* Assuming the base is a Map, this method will always return Object.class. This is because * Maps accept any object as a key. *

* * @param context The context of this evaluation. * @param base The map to analyze. Only bases of type Map are handled by this resolver. * @return null if base is not a Map; otherwise Object.class. */ @Override public Class getCommonPropertyType(ELContext context, Object base) { if (base != null && base instanceof Map) { return Object.class; } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy