| /******************************************************************************* |
| * Copyright (c) 2001, 2007 Oracle Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Oracle Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.jsf.validation.internal.el.operators; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jst.jsf.common.internal.types.LiteralType; |
| import org.eclipse.jst.jsf.common.internal.types.SignatureBasedType; |
| import org.eclipse.jst.jsf.common.internal.types.TypeCoercer; |
| import org.eclipse.jst.jsf.common.internal.types.TypeCoercionException; |
| import org.eclipse.jst.jsf.common.internal.types.TypeConstants; |
| import org.eclipse.jst.jsf.common.internal.types.TypeTransformer; |
| import org.eclipse.jst.jsf.common.internal.types.ValueType; |
| import org.eclipse.jst.jsf.context.symbol.IObjectSymbol; |
| import org.eclipse.jst.jsf.context.symbol.ISymbol; |
| import org.eclipse.jst.jsf.context.symbol.internal.util.IObjectSymbolBasedValueType; |
| import org.eclipse.jst.jsf.designtime.DesignTimeApplicationManager; |
| import org.eclipse.jst.jsf.designtime.el.AbstractDTPropertyResolver; |
| import org.eclipse.jst.jsf.designtime.resolver.ISymbolContextResolver; |
| import org.eclipse.jst.jsf.validation.internal.el.diagnostics.DiagnosticFactory; |
| |
| /** |
| * Handles the operator 'bracket' where bracket(expr-a, id-b) == 'expr-a[id-b]' in EL syntax |
| * |
| * @author cbateman |
| * |
| */ |
| public class BracketOperator extends MemberAccessorOperator |
| { |
| private static final String OPERATOR_NAME_ARRAY_ACCESSOR = "array ('[]') accessor"; //$NON-NLS-1$ |
| |
| /** |
| * @param diagnosticFactory |
| * @param file |
| * @param resolver |
| */ |
| public BracketOperator(final DiagnosticFactory diagnosticFactory, final IFile file, final ISymbolContextResolver resolver) |
| { |
| super(file, diagnosticFactory, resolver); |
| } |
| |
| protected SignatureBasedType handlePerformObjectSymbolValue( |
| IObjectSymbolBasedValueType firstArg, ValueType secondArg) |
| { |
| // per JSP.2.3.4 step 5.2, if value-a is a list or array access |
| if (firstArg.isInstanceOf(TypeConstants.TYPE_LIST) |
| || Signature.getArrayCount(firstArg.getSignature()) > 0) |
| { |
| return handlePerformNumericPropertyAccessorBase(firstArg, secondArg); |
| } |
| |
| // per JSP.2.3.4 step 5, if value-a is a map or if it is not |
| // a list or array (and therefore a bean), treat it as named property accessed object |
| // if firstArg is a map then we must treat the access like a map.get(secondArg) |
| |
| // if we don't have a literal value with which to derive value-b, then |
| // we can't get a property |
| if (secondArg instanceof LiteralType) |
| { |
| return handlePerformNamedPropertyAccessorBase(firstArg, (LiteralType)secondArg); |
| } |
| |
| return null; |
| } |
| |
| protected Diagnostic validateObjectSymbolValue(IObjectSymbolBasedValueType firstArg, |
| ValueType secondArg) |
| { |
| // per JSP.2.3.4 step 5.2, if value-a is a list or array access |
| if (firstArg.isInstanceOf(TypeConstants.TYPE_LIST) |
| || firstArg.getSymbol().getTypeDescriptor().isArray()) |
| { |
| return validateNumericPropertyAccessorBase(firstArg, secondArg); |
| } |
| |
| // per JSP.2.3.4 step 5, if value-a is a map or if it is not |
| // a list or array (and therefore a bean), treat it as named property accessed object |
| // if firstArg is a map then we must treat the access like a map.get(secondArg) |
| if (secondArg instanceof LiteralType) |
| { |
| return validateNamedPropertyAccessorBase(firstArg, (LiteralType) secondArg); |
| } |
| // otherwise, there's nothing we can guarantee |
| return Diagnostic.OK_INSTANCE; |
| } |
| |
| private Diagnostic validateNumericPropertyAccessorBase(IObjectSymbolBasedValueType firstArg, |
| ValueType secondArg) |
| { |
| try |
| { |
| // secondArg must successfully coerce to integer |
| TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(secondArg.getSignature())); |
| |
| if (secondArg instanceof LiteralType) |
| { |
| // this will throw a TypeCoercionExceptino if it won't |
| // coerce |
| Integer integerValue = |
| (Integer) ((LiteralType)secondArg).coerceToNumber(Integer.class); |
| |
| if (integerValue.intValue() < 0) |
| { |
| return _diagnosticFactory.create_POSSIBLE_ARRAY_INDEX_OUT_OF_BOUNDS(integerValue); |
| } |
| } |
| else |
| { |
| // if the argument is a non-literal string, we can't verify |
| // that the coercion to integer won't throw an exception |
| // at runtime |
| if (TypeCoercer.typeIsString(secondArg.getSignature())) |
| { |
| return _diagnosticFactory.create_UNARY_OP_STRING_CONVERSION_NOT_GUARANTEED(OPERATOR_NAME_ARRAY_ACCESSOR); |
| } |
| } |
| |
| // TODO: attempt to detect ArrayIndexOutOfBoundsException |
| return Diagnostic.OK_INSTANCE; |
| } |
| catch (TypeCoercionException e) |
| { |
| return _diagnosticFactory.create_BINARY_OP_COULD_NOT_MAKE_NUMERIC_COERCION(OPERATOR_NAME_ARRAY_ACCESSOR); |
| } |
| } |
| |
| private SignatureBasedType handlePerformNumericPropertyAccessorBase(IObjectSymbolBasedValueType firstArg, |
| ValueType secondArg) |
| { |
| AbstractDTPropertyResolver propResolver = getPropertyResolver(); |
| int index = 0; |
| if (secondArg instanceof LiteralType) |
| { |
| try { |
| index = ((LiteralType)secondArg).coerceToNumber(Integer.class).intValue(); |
| } catch (TypeCoercionException e) { |
| // suppress, just use index = 0 |
| // this maybe should be an assertion... |
| } |
| } |
| |
| final ISymbol symbol = |
| propResolver.getProperty(firstArg.getSymbol(), index); |
| |
| if (symbol instanceof IObjectSymbol) |
| { |
| return IObjectSymbolBasedValueType.getInstance(symbol); |
| } |
| |
| // if can't be resolved, return null |
| return null; |
| } |
| |
| @Override |
| protected String getOperatorName() |
| { |
| return Messages.getString("BracketOperator.Name"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * @return the property resolver for the current source file |
| */ |
| private AbstractDTPropertyResolver getPropertyResolver() |
| { |
| final DesignTimeApplicationManager manager = |
| DesignTimeApplicationManager.getInstance(_file.getProject()); |
| |
| if (manager != null) |
| { |
| return manager.getPropertyResolver(); |
| } |
| |
| return null; |
| } |
| } |