blob: 37390d96fcc1397aba7b42ac7d7738f3c659156d [file] [log] [blame]
/*******************************************************************************
* 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.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(ValueType firstArg, 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(IObjectSymbolBasedValueType firstArg
, 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(ValueType firstArg, 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(IObjectSymbolBasedValueType firstArg
, 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)
{
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)
{
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()
{
return
DesignTimeApplicationManager.getInstance(_file.getProject())
.getPropertyResolver();
}
/**
* @return the method resolver for the current source file
*/
protected final AbstractDTMethodResolver getMethodResolver()
{
return
DesignTimeApplicationManager.getInstance(_file.getProject())
.getMethodResolver();
}
/**
* @return a user-readable name of the operator (i.e. dot or bracket)
*/
protected abstract String getOperatorName();
}