blob: a4dfba4127c3006c5cbf55588a865d44a893a220 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Oracle Corporation.
* 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:
* Cameron Bateman/Oracle - initial API and implementation
*
********************************************************************************/
package org.eclipse.jst.jsf.designtime.symbols;
import java.util.Arrays;
import java.util.HashMap;
import org.eclipse.core.resources.IFile;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jst.jsf.common.internal.types.TypeConstants;
import org.eclipse.jst.jsf.common.internal.types.ValueType;
import org.eclipse.jst.jsf.common.util.TypeUtil;
import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContext;
import org.eclipse.jst.jsf.context.symbol.ERuntimeSource;
import org.eclipse.jst.jsf.context.symbol.IComponentSymbol;
import org.eclipse.jst.jsf.context.symbol.IJavaTypeDescriptor2;
import org.eclipse.jst.jsf.context.symbol.IMapTypeDescriptor;
import org.eclipse.jst.jsf.context.symbol.ISymbol;
import org.eclipse.jst.jsf.context.symbol.SymbolFactory;
import org.eclipse.jst.jsf.core.internal.JSFCorePlugin;
import org.eclipse.jst.jsf.core.internal.tld.IJSFConstants;
import org.eclipse.jst.jsf.validation.internal.ValidationPreferences;
import org.eclipse.jst.jsf.validation.internal.appconfig.AppConfigValidationUtil;
import org.eclipse.jst.jsf.validation.internal.el.ELExpressionValidator;
import org.eclipse.jst.jsf.validation.internal.el.IExpressionSemanticValidator;
import org.w3c.dom.Element;
/**
* A framework provided variable factory for EL model objects that are
* constructed based on DataModel's dervied from an EL expression.
*
* @author cbateman
*
*/
public abstract class AbstractDataModelVariableFactory
{
/**
* @param elText The EL expression text. Must not be null
* @param elContext The document context pointing to elText in the source document. Must not be null
* @param file The workspace resource that contains elText. Must not be null.
* @return the value expression resolved from elText or null if it cannot
* be resolved or elText doesn't resolve to value expression (i.e. is a method expression)
*/
public final ValueType createValueExpression(final String elText, final IStructuredDocumentContext elContext, final IFile file)
{
assert elText != null;
assert elContext != null;
assert file != null;
final ValidationPreferences prefs =
new ValidationPreferences(JSFCorePlugin.getDefault().getPreferenceStore());
prefs.load();
final ELExpressionValidator validator =
new ELExpressionValidator(elContext, elText, file, prefs.getElPrefs());
validator.validateXMLNode();
final IExpressionSemanticValidator semValidator =
validator.getSemanticValidator();
if (semValidator != null
&& semValidator.getExpressionType() instanceof ValueType)
{
return (ValueType) semValidator.getExpressionType();
}
return null;
}
/**
* @param symbolName The name of the symbol to be created. Must not be null
* @param signature The type signature of the array type. Must not be null
* @param javaProject must not be null
* @return a symbol based approximating an implicit DataModel wrapper for an array
*/
protected final ISymbol createArraySymbol(final String symbolName, final String signature, final IJavaProject javaProject)
{
assert symbolName != null;
assert signature != null;
assert javaProject != null;
final String arrayElementType = Signature.getElementType(signature);
final int arrayCount = Signature.getArrayCount(signature);
String adjustedSignature = null;
// if it is a single array nesting, then it will just be the element type,
// but if it is a multi-dim array, then the scalar element will be an array
// with one less nesting level. It's a strange corner case to have an implicit
// array of something as a row type, but it is a valid case. I suppose
// it may be happen if you want to have tables of tables in which the nested tables
// in turn use nested variables as their type...
if (arrayCount > 0)
{
adjustedSignature = Signature.createArraySignature(arrayElementType, arrayCount-1);
}
else
{
adjustedSignature = arrayElementType;
}
return createScalarSymbol(symbolName, adjustedSignature, javaProject);
}
/**
* Implements default rules for taking an non-array value expression
* and resolving it to a ISymbol modelling a JSF DataModel
*
* @param symbolName
* @param valueType
* @param javaProject
* @return a symbol where valueType is considered to be the base type upon
* which a data model would be created. This combines the possibility that the
* value expression is either an explicit DataModel implementation or a
* non-DataModel type for which JSF be default provides an implicit wrapper model.
*
* List is treated as a special case here, since their are two subcases:
* 1) the list is a raw type, in which case it must be treated as implicit and opaque (as to the type of the variable created)
* 2) the list has Java 5 type argument information that can be used to infer the type of the row variable
*/
public ISymbol createFromType(String symbolName, ValueType valueType,
IJavaProject javaProject)
{
return internalCreateFromBaseType(symbolName, valueType, javaProject);
}
private ISymbol internalCreateFromBaseType(String symbolName, ValueType valueType,
IJavaProject javaProject)
{
// based on JSF 1.1 spec section 4.2.1.4 the data model
// value binding can be one of a number of object that will
// get an implicit DataModel wrapper at runtime
// could be an array
if (Signature.getArrayCount(valueType.getSignature())>0)
{
return createArraySymbol(symbolName, valueType.getSignature(), javaProject);
}
// if is a list, then we have extra work to do if it
// is generic and has info about its contents
if (valueType.isInstanceOf(TypeConstants.TYPE_LIST))
{
return createFromList(symbolName, valueType, javaProject);
}
// if is JSTL ResultSet, java ResultSet or DataModel
// return the default symbol -- in the absence of definite
// template info, these row containers are opaque to us
else if (valueType.isInstanceOf(TypeConstants.TYPE_JAVAX_SERVLET_JSP_JSTL_SQL_RESULT)
|| valueType.isInstanceOf(TypeConstants.TYPE_RESULT_SET)
|| valueType.isInstanceOf(TypeConstants.TYPE_DATA_MODEL))
{
return createDefaultSymbol(symbolName);
}
// in other cases, we assume that the value is an explicit single row
// scalar object
return createScalarSymbol(symbolName, valueType.getSignature(), javaProject);
}
/**
* @param symbolName The name of the symbol to create. Must not be null.
* @param valueType The value expression representing the implicit list. The signature
* on the valueType must be a list. Must not be null.
* @param javaProject The JavaProject whose classpath will be used to resolve types. Must not be null.
*
* @return a symbol that approximates as best as possible an implicit DataModel for java.util.List value expressions. If the List has
* resolvable Java 5 type arguments, then a scalar symbol will be created
* using this type information. If it is a raw type, then
* createDefaultSymbol() is called
*/
protected final ISymbol createFromList(String symbolName, ValueType valueType, IJavaProject javaProject)
{
assert symbolName != null;
assert valueType != null;
assert javaProject != null;
assert TypeConstants.TYPE_LIST.equals(valueType.getSignature());
final String[] typeArguments = valueType.getTypeArguments();
if (typeArguments != null && typeArguments.length > 0)
{
// a list has a single type argument
final String typeArg = typeArguments[0];
if (Signature.getTypeSignatureKind(typeArg) == Signature.CLASS_TYPE_SIGNATURE)
{
return createScalarSymbol(symbolName, typeArg, javaProject);
}
}
// if no resolvable type signatures, do the default thing
return createDefaultSymbol(symbolName);
}
/**
* @param symbolName The name of the symbol to create. Must not be null.
* @param signature The fully resolved type signature of the scalar. Must not be null.
* @param javaProject The JavaProject whose classpath is to be used to resolve type information for signture. Must not be null.
* @return a symbol approximating a scalar object DataModel wrapper. The row variable for the
* data model becomes of type signature
*/
protected final ISymbol createScalarSymbol(final String symbolName, final String signature, final IJavaProject javaProject)
{
assert symbolName != null;
assert signature != null;
assert javaProject != null;
final String elementType = Signature.getElementType(signature);
IJavaTypeDescriptor2 desc =
SymbolFactory.eINSTANCE.createIJavaTypeDescriptor2();
final int arrayCount = Signature.getArrayCount(signature);
if (arrayCount > 0)
{
desc.setArrayCount(arrayCount);
}
IType type = TypeUtil.resolveType(javaProject, elementType);
if (type != null)
{
desc.setType(type);
}
else
{
desc.setTypeSignatureDelegate(Signature.getTypeErasure(signature));
}
desc.getTypeParameterSignatures().addAll(Arrays.asList(Signature.getTypeArguments(signature)));
IComponentSymbol symbol = SymbolFactory.eINSTANCE.createIComponentSymbol();
symbol.setName(symbolName);
symbol.setTypeDescriptor(desc);
symbol.setRuntimeSource(ERuntimeSource.TAG_INSTANTIATED_SYMBOL_LITERAL);
return symbol;
}
/**
* @param symbolName The name of the symbol to create. Must not be null
* @return a default symbol that eliminates bogus warnings for this dataTable's
* row variable in cases where something better is resolvable. Note that this is
* not ideal, since will result in any property being accepted on the variable with
* this name.
*/
public final ISymbol createDefaultSymbol(final String symbolName)
{
assert symbolName != null;
final IMapTypeDescriptor typeDesc =
SymbolFactory.eINSTANCE.createIBoundedMapTypeDescriptor();
// empty map source
typeDesc.setMapSource(new HashMap());
final IComponentSymbol symbol =
SymbolFactory.eINSTANCE.createIComponentSymbol();
symbol.setName(symbolName);
symbol.setTypeDescriptor(typeDesc);
symbol.setDetailedDescription(Messages.getString("AbstractDataModelVariableFactory.DataModel.Symbol.RowVariable.DetailedDescription")); //$NON-NLS-1$
symbol.setRuntimeSource(ERuntimeSource.TAG_INSTANTIATED_SYMBOL_LITERAL);
return symbol;
}
/**
* @param dataTableElement the DOM element that has a "value" attribute.
* Must not be null.
* @return the el text from the 'value attribute of a dataTable element
* or null if not found
*/
protected static String getELText(final Element dataTableElement)
{
assert dataTableElement != null;
String attrVal = dataTableElement.getAttribute(IJSFConstants.ATTR_VALUE);
if (attrVal != null)
{
return AppConfigValidationUtil.extractELExpression(attrVal).getElText();
}
return null;
}
/**
* @return the variable source name. Protects against null in the abstract method
*/
protected final String internalGetVariableSourceName()
{
String variableSourceName = getVariableSourceName();
if (variableSourceName == null)
{
JSFCorePlugin.log("Missing variableSourceName", new Throwable()); //$NON-NLS-1$
return "**missing variable source name**"; //$NON-NLS-1$
}
return variableSourceName;
}
/**
* @return a user displayable name for the source of variables created by this factory
* Must not return null.
*/
protected abstract String getVariableSourceName();
}