| /******************************************************************************* |
| * Copyright (c) 2001, 2008 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.jst.jsf.common.internal.types.LiteralType; |
| import org.eclipse.jst.jsf.common.internal.types.NullLiteralType; |
| 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.TypeConstants; |
| import org.eclipse.jst.jsf.common.internal.types.ValueType; |
| import org.eclipse.jst.jsf.context.symbol.IMethodSymbol; |
| import org.eclipse.jst.jsf.context.symbol.IObjectSymbol; |
| import org.eclipse.jst.jsf.context.symbol.IPropertySymbol; |
| import org.eclipse.jst.jsf.context.symbol.ISymbol; |
| import org.eclipse.jst.jsf.context.symbol.internal.util.IMethodSymbolBasedType; |
| import org.eclipse.jst.jsf.context.symbol.internal.util.IObjectSymbolBasedValueType; |
| import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; |
| import org.eclipse.jst.jsf.designtime.DesignTimeApplicationManager; |
| import org.eclipse.jst.jsf.designtime.el.AbstractDTMethodResolver; |
| import org.eclipse.jst.jsf.designtime.el.AbstractDTPropertyResolver; |
| import org.eclipse.jst.jsf.validation.internal.el.diagnostics.DiagnosticFactory; |
| |
| /** |
| * Super-class for all operators whose function is to access members of an EL |
| * object. i.e. the "." and "[]" operators |
| * |
| * @author cbateman |
| * |
| */ |
| public abstract class MemberAccessorOperator |
| { |
| /** |
| * The source file for the EL expression in which this operator is being |
| * evaluated. |
| */ |
| protected final IFile _file; |
| |
| /** |
| * the common factory used to create diagnostics |
| */ |
| protected final DiagnosticFactory _diagnosticFactory; |
| |
| // TODO: need to reconcile with BinaryOperator? performOperation must return |
| // SignatureBasedType since it may return a method. This can't happen |
| // with other operators (besides eqiv []) |
| /** |
| * @param file |
| * @param diagnosticFactory |
| */ |
| protected MemberAccessorOperator(final IFile file, |
| final DiagnosticFactory diagnosticFactory) |
| { |
| _file = file; |
| _diagnosticFactory = diagnosticFactory; |
| } |
| |
| /** |
| * @param firstArg |
| * @param secondArg |
| * @return the result of validating the dot operation with these arguments. |
| */ |
| public Diagnostic validate(final ValueType firstArg, final ValueType secondArg) |
| { |
| if (!(firstArg instanceof IObjectSymbolBasedValueType)) |
| { |
| throw new AssertionError( |
| "The first argument of the member operator must always be a symbol resolvable value type"); |
| } |
| |
| if (TypeCoercer.typeIsNull(secondArg.getSignature())) |
| { |
| return _diagnosticFactory |
| .create_BINARY_OP_DOT_WITH_VALUEB_NULL(getOperatorName()); |
| } |
| |
| return validateObjectSymbolValue( |
| (IObjectSymbolBasedValueType) firstArg, secondArg); |
| } |
| |
| /** |
| * @param firstArg |
| * @param secondArg |
| * @return the diagnostic for member(firstArg, secondArg) |
| */ |
| protected abstract Diagnostic validateObjectSymbolValue( |
| IObjectSymbolBasedValueType firstArg, ValueType secondArg); |
| |
| /** |
| * @param firstArg |
| * @param secondArg |
| * @return a validation of a named property accessible base (map or bean) |
| * given an a literal key argument |
| */ |
| protected Diagnostic validateNamedPropertyAccessorBase( |
| final IObjectSymbolBasedValueType firstArg, final LiteralType secondArg) |
| { |
| final IObjectSymbol curBaseSymbol = firstArg.getSymbol(); |
| |
| final ISymbol nextSymbol = |
| getMemberSymbol(firstArg.getSymbol(), secondArg |
| .getLiteralValueRaw()); |
| |
| // if the x in x.y is an unconstrained map an it returns |
| // a java.lang.Object, then return null. We can't really say |
| // anything meaningful about such a property anyway. |
| // TODO: do we need to refine the type descriptor on such |
| // a property object to make this more precise? |
| if (curBaseSymbol.supportsCoercion(TypeConstants.TYPE_MAP) |
| && nextSymbol instanceof IPropertySymbol |
| && TypeConstants.TYPE_JAVAOBJECT |
| .equals(((IPropertySymbol) nextSymbol) |
| .getTypeDescriptor().getTypeSignature())) |
| { |
| // if we get a symbol back that's a generic object coming from a map |
| // then stop validating; we can't tell anything for sure |
| return Diagnostic.OK_INSTANCE; |
| } |
| |
| if (nextSymbol == null) |
| { |
| return _diagnosticFactory.create_MEMBER_NOT_FOUND(secondArg |
| .getLiteralValue(), firstArg.getSymbol().getName()); |
| } |
| |
| return Diagnostic.OK_INSTANCE; |
| } |
| |
| /** |
| * @param firstArg |
| * @param secondArg |
| * @return the resolved type for the operation or null if not computable |
| */ |
| public SignatureBasedType performOperation(final ValueType firstArg, |
| final ValueType secondArg) |
| { |
| if (!(firstArg instanceof IObjectSymbolBasedValueType)) |
| { |
| return null; |
| } |
| |
| // per JSP.2.3.4, if value-b is null, then return null (not literal |
| // null) |
| if (TypeCoercer.typeIsNull(secondArg.getSignature())) |
| { |
| return null; |
| } |
| |
| return handlePerformObjectSymbolValue( |
| (IObjectSymbolBasedValueType) firstArg, secondArg); |
| } |
| |
| /** |
| * @param firstArg -- |
| * represents value-a (expr-a after step 1) in JSP.2.3.4 |
| * @param secondArg -- |
| * represents value-b (expr-b after step 3) in JSP.2.3.4 |
| * @return the new ValueType for this operation or null |
| */ |
| protected abstract SignatureBasedType handlePerformObjectSymbolValue( |
| IObjectSymbolBasedValueType firstArg, ValueType secondArg); |
| |
| /** |
| * @param firstArg |
| * @param secondArg |
| * @return the resolved type for firstArg[secondArg] treating firstArg as a |
| * type that uses a named property accessor (i.e. a map or bean but |
| * not a list or array) or null if unresolved |
| */ |
| protected SignatureBasedType handlePerformNamedPropertyAccessorBase( |
| final IObjectSymbolBasedValueType firstArg, final LiteralType secondArg) |
| { |
| final ISymbol symbol = |
| getMemberSymbol(firstArg.getSymbol(), secondArg |
| .getLiteralValueRaw()); |
| |
| if (symbol instanceof IPropertySymbol) |
| { |
| return new IObjectSymbolBasedValueType((IPropertySymbol) symbol); |
| } |
| else if (symbol instanceof IMethodSymbol) |
| { |
| return new IMethodSymbolBasedType((IMethodSymbol) symbol); |
| } |
| |
| // fall-through and return null |
| // per JSP2.3.4 steps 5 and 6, return null literal if map, null (error) |
| // otherwise |
| if (firstArg.isInstanceOf(TypeConstants.TYPE_MAP)) |
| { |
| return NullLiteralType.SINGLETON; |
| } |
| return null; |
| } |
| |
| /** |
| * @param symbol |
| * @param name |
| * @return the member symbol of 'symbol' corresponding to 'name' or null if |
| * there is no such member |
| */ |
| protected final ISymbol getMemberSymbol(final IObjectSymbol symbol, |
| final Object name) |
| { |
| ISymbol memberSymbol = getPropertySymbol(symbol, name); |
| |
| if (memberSymbol != null) |
| { |
| return memberSymbol; |
| } |
| |
| memberSymbol = getMethodSymbol(symbol, name); |
| |
| // otherwise, see if it's a valid method |
| if (memberSymbol != null) |
| { |
| return memberSymbol; |
| } |
| |
| // if not a property or method, then not a valid member |
| return null; |
| } |
| |
| /** |
| * @param symbol |
| * @param name |
| * @return the property symbol called name relative to 'symbol' or null if |
| * one doesn't exist |
| */ |
| protected final ISymbol getPropertySymbol(final ISymbol symbol, |
| final Object name) |
| { |
| final AbstractDTPropertyResolver resolver = getPropertyResolver(); |
| |
| if (resolver != null) |
| { |
| return resolver.getProperty(symbol, name); |
| } |
| |
| JSFCorePlugin.log("Error acquiring property resolver", new Throwable()); |
| return null; |
| } |
| |
| /** |
| * @param symbol |
| * @param name |
| * @return the method symbol on 'symbol' corresponding to 'name' or null if |
| * no such member |
| */ |
| protected final IMethodSymbol getMethodSymbol(final IObjectSymbol symbol, |
| final Object name) |
| { |
| final AbstractDTMethodResolver resolver = getMethodResolver(); |
| |
| if (resolver != null) |
| { |
| return resolver.getMethod(symbol, name); |
| } |
| |
| JSFCorePlugin.log("Error acquiring property resolver", new Throwable()); |
| return null; |
| |
| } |
| |
| /** |
| * @return the property resolver for the current source file |
| */ |
| protected final AbstractDTPropertyResolver getPropertyResolver() |
| { |
| final DesignTimeApplicationManager manager = |
| DesignTimeApplicationManager.getInstance(_file.getProject()); |
| |
| if (manager != null) |
| { |
| return manager.getPropertyResolver(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @return the method resolver for the current source file |
| */ |
| protected final AbstractDTMethodResolver getMethodResolver() |
| { |
| final DesignTimeApplicationManager manager = |
| DesignTimeApplicationManager.getInstance(_file.getProject()); |
| |
| if (manager != null) |
| { |
| return manager.getMethodResolver(); |
| } |
| return null; |
| } |
| |
| /** |
| * @return a user-readable name of the operator (i.e. dot or bracket) |
| */ |
| protected abstract String getOperatorName(); |
| } |