| /******************************************************************************* |
| * Copyright (c) 2008 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: |
| * Matthias Fuessel -- extracted from https://bugs.eclipse.org/bugs/show_bug.cgi?id=215461 |
| * Cameron Bateman/Oracle - integrated and moved data table code here. |
| * |
| ********************************************************************************/ |
| |
| package org.eclipse.jst.jsf.designtime.symbols; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.emf.common.util.ECollections; |
| 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.IBeanInstanceSymbol; |
| import org.eclipse.jst.jsf.context.symbol.IComponentSymbol; |
| import org.eclipse.jst.jsf.context.symbol.IInstanceSymbol; |
| import org.eclipse.jst.jsf.context.symbol.IJavaTypeDescriptor2; |
| import org.eclipse.jst.jsf.context.symbol.IMapTypeDescriptor; |
| import org.eclipse.jst.jsf.context.symbol.IObjectSymbol; |
| import org.eclipse.jst.jsf.context.symbol.ISymbol; |
| import org.eclipse.jst.jsf.context.symbol.InitializedSymbolFactory; |
| import org.eclipse.jst.jsf.context.symbol.SymbolFactory; |
| import org.eclipse.jst.jsf.context.symbol.internal.util.IObjectSymbolBasedValueType; |
| import org.eclipse.jst.jsf.designtime.resolver.IStructuredDocumentSymbolResolverFactory; |
| import org.eclipse.jst.jsf.designtime.resolver.StructuredDocumentSymbolResolverFactory; |
| import org.eclipse.jst.jsf.validation.internal.IJSFViewValidator.IValidationReporter; |
| import org.eclipse.jst.jsf.validation.internal.IJSFViewValidator.ReporterAdapter; |
| import org.eclipse.jst.jsf.validation.internal.el.ELExpressionValidator; |
| import org.eclipse.jst.jsf.validation.internal.el.IExpressionSemanticValidator; |
| |
| /** |
| * Factory for creating symbols for variables/message bundles that are |
| * contributed by components/tags. This Factory can be used by descendants of |
| * {@link org.eclipse.jst.jsf.context.symbol.source.AbstractContextSymbolFactory} in order to create the symbols they want |
| * to contribute. |
| * |
| * It provides also methods for getting the resulting {@link ValueType} from an |
| * EL expression and the "row" type signature from an DataModel/Array/List |
| * {@link ValueType}. |
| * |
| * NOTE: C.B March 26, 2008 -- commented out portions are left for the future |
| * because the rely on enhancements to the symbol model that could not be made |
| * in the current release. Other portions of the original ComponentSymbolFactory |
| * have been moved to {@link org.eclipse.jst.jsf.context.symbol.InitializedSymbolFactory} |
| * This reflects the split between things deemed to be generic EL (put in common |
| * plugin) and things deemed to be JSF-specific (put here). |
| * |
| */ |
| public final class JSFSymbolFactory extends InitializedSymbolFactory |
| { |
| /** |
| * @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. |
| * @param symbolResolverFactory |
| * @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 ValueType getValueTypeFromEL(final String elText, |
| final IStructuredDocumentContext elContext, final IFile file, IStructuredDocumentSymbolResolverFactory symbolResolverFactory) |
| { |
| assert elText != null; |
| assert elContext != null; |
| assert file != null; |
| |
| // TODO investigate if infinite recursion; use ASTResolver instead?? |
| final IValidationReporter reporter = new ReporterAdapter(); |
| final ELExpressionValidator validator = |
| new ELExpressionValidator( |
| elContext, elText, symbolResolverFactory, reporter); |
| validator.validateXMLNode(); |
| final IExpressionSemanticValidator semValidator = validator |
| .getSemanticValidator(); |
| if (semValidator != null |
| && semValidator.getExpressionType() instanceof ValueType) |
| { |
| return (ValueType) semValidator.getExpressionType(); |
| } |
| return null; |
| } |
| |
| /** |
| * Convenience for {@link JSFSymbolFactory#getValueTypeFromEL(String, IStructuredDocumentContext, IFile, IStructuredDocumentSymbolResolverFactory)} |
| * using StructuredDocumentSymbolResolverFactory.getInstance(). |
| * |
| * @param elText |
| * @param elContext |
| * @param file |
| * @return a ValueType or null |
| */ |
| public ValueType getValueTypeFromEL(final String elText, |
| final IStructuredDocumentContext elContext, final IFile file) |
| { |
| return getValueTypeFromEL(elText, elContext, file, StructuredDocumentSymbolResolverFactory.getInstance()); |
| } |
| |
| /** |
| * @param type |
| * @return <code>true</code>, if the given {@link ValueType} represents |
| * an instance of <code>javax.faces.model.DataModel</code> |
| */ |
| private boolean isFacesDataModel(ValueType type) |
| { |
| return type.isInstanceOf(TypeConstants.TYPE_DATA_MODEL); |
| } |
| |
| /** |
| * Tries to guess the row type of a <code>javax.faces.DataModel</code> |
| * instance. This will only work if <code>type</code> is a descendant that |
| * narrows down the return type of "getRowData()". |
| * |
| * @param type |
| * @return the row type of the given DataModel. Will return |
| * <code>null</code> if <code>type</code> is no DataModel or if |
| * nothing more specific than <code>Object</code> can be |
| * determined |
| */ |
| public String getRowSignatureFromDataModel(ValueType type) |
| { |
| if (type instanceof IObjectSymbolBasedValueType) |
| { |
| ISymbol resSymbol = ((IObjectSymbolBasedValueType) type) |
| .getSymbol().call( |
| "getRowData", ECollections.emptyEList(), "res"); //$NON-NLS-1$ //$NON-NLS-2$ |
| if (resSymbol != null && resSymbol instanceof IObjectSymbol) |
| { |
| // TODO full signature |
| final String signature = ((IObjectSymbol) resSymbol) |
| .getTypeDescriptor().getTypeSignature(); |
| if (!TypeConstants.TYPE_JAVAOBJECT.equals(signature)) |
| { |
| return signature; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @param type |
| * @return <code>true</code>, if <code>type</code> is a collection or |
| * array |
| */ |
| public boolean isContainerType(ValueType type) |
| { |
| return type.isArray() |
| || type.isInstanceOf(TypeConstants.TYPE_COLLECTION); |
| } |
| |
| /** |
| * @param type - |
| * the type of the <code>value</code> property |
| * @return the type signature of the row variable for every type that |
| * <code>UIData</code> takes as <code>value</code> property. |
| * Will return <code>null</code> if no type or nothing more |
| * specific than <code>Object</code> can be determined |
| */ |
| public String getRowSignatureFromValueType(ValueType type) |
| { |
| if (isContainerType(type)) |
| { |
| return getElementSignatureFromContainerType(type); |
| } |
| if (isFacesDataModel(type)) |
| { |
| return getRowSignatureFromDataModel(type); |
| } |
| // Otherwise, according to jsf spec, treat value as single row: |
| // TODO full signature |
| return type.getSignature(); |
| } |
| |
| /** |
| * @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 source the runtime source |
| * @param javaProject must not be null |
| * @return a symbol based approximating an implicit DataModel wrapper for an array |
| */ |
| public final ISymbol createArraySymbol(final String symbolName, final String signature, final ERuntimeSource source, 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, source, javaProject); |
| } |
| |
| /** |
| * @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 source |
| * @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 |
| */ |
| public final ISymbol createScalarSymbol(final String symbolName, final String signature, final ERuntimeSource source, 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. |
| * @param valueType The value expression representing the implicit list. The signature |
| * on the valueType must be a list. Must not be null. |
| * @param source |
| * @param description |
| * @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 |
| */ |
| public final ISymbol createFromList(String symbolName, ValueType valueType, ERuntimeSource source, String description, 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, source, javaProject); |
| } |
| } |
| |
| // if no resolvable type signatures, do the default thing |
| return createDefaultSymbol(symbolName, ERuntimeSource.TAG_INSTANTIATED_SYMBOL_LITERAL, description); |
| } |
| |
| /** |
| * @param symbolName The name of the symbol to create. Must not be null |
| * @param source |
| * @param description |
| * @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, final ERuntimeSource source, final String description) |
| { |
| 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(description); //$NON-NLS-1$ |
| symbol.setRuntimeSource(source); |
| return symbol; |
| } |
| |
| /** |
| * FUTURE use: added to support future API feature. Should not be used. |
| * @param symbolName |
| * @param bundleName |
| * @param project |
| * @return an instance symbol for the message bundle |
| */ |
| public IInstanceSymbol createMessageBundleSymbol(final String symbolName, final String bundleName, IJavaProject project) |
| { |
| // FUTURE USE |
| throw new UnsupportedOperationException("see https://bugs.eclipse.org/bugs/show_bug.cgi?id=215461"); |
| } |
| |
| /** |
| * FUTURE use: added to support future API feature. Should not be used. |
| * @param symbolName |
| * @param fullyQualifiedName |
| * @param source |
| * @param description |
| * @param javaProject |
| * @return an instance symbol for the message bundle |
| */ |
| public IBeanInstanceSymbol createManagedBeanSymbol(final String symbolName, final String fullyQualifiedName, |
| ERuntimeSource source, final String description, final IJavaProject javaProject) |
| { |
| // FUTURE USE |
| throw new UnsupportedOperationException("see https://bugs.eclipse.org/bugs/show_bug.cgi?id=215461"); |
| } |
| |
| // / /** |
| // * @param symbolName |
| // * @param signature |
| // * @param javaProject |
| // * @return a symbol for a bean |
| // */ |
| // public IComponentBeanSymbol createBeanSymbolFromSignature( |
| // final String symbolName, final String signature, |
| // final IJavaProject javaProject) |
| // { |
| // IJavaTypeDescriptor2 desc = createTypeDescriptorFromSignature( |
| // signature, javaProject); |
| // |
| // IComponentBeanSymbol symbol = SymbolFactory.eINSTANCE |
| // .createIComponentBeanSymbol(); |
| // symbol.setName(symbolName); |
| // symbol.setTypeDescriptor(desc); |
| // symbol.setRuntimeSource(ERuntimeSource.TAG_INSTANTIATED_SYMBOL_LITERAL); |
| // return symbol; |
| // } |
| |
| // /** |
| // * @param symbolName |
| // * @param bundleName |
| // * @param project |
| // * @return a symbol for a message bundle |
| // */ |
| // public IMessageBundleSymbol createMessageBundleSymbolFromBundleName(final String symbolName, final String bundleName, IJavaProject project) { |
| // final IMessageBundleTypeDescriptor typeDesc = SymbolFactory.eINSTANCE.createIMessageBundleTypeDescriptor(); |
| // try { |
| // Map mapSource = ResourceBundleMapSourceFactory.getResourceBundleMapSource(project.getProject(), bundleName); |
| // typeDesc.setMapSource(mapSource); |
| // } catch (JavaModelException e) { |
| // JSFCorePlugin.log(e, "Error creating resource map for bundle '"bundleName "'"); //$NON-NLS-1$ //$NON-NLS-2$ |
| // } catch (IOException e) { |
| // JSFCorePlugin.log(e, "Error creating resource map for bundle '"bundleName "'"); //$NON-NLS-1$ //$NON-NLS-2$ |
| // } catch (CoreException e) { |
| // JSFCorePlugin.log(e, "Error creating resource map for bundle '"bundleName "'"); //$NON-NLS-1$ //$NON-NLS-2$ |
| // } |
| // final IMessageBundleSymbol symbol = SymbolFactory.eINSTANCE.createIMessageBundleSymbol(); |
| // symbol.setName(symbolName); |
| // symbol.setTypeDescriptor(typeDesc); |
| // symbol.setDetailedDescription(NLS.bind(Messages.getString("ComponentSymbolFactory.IMessageBundleSymbol.detailedDescription"), bundleName)); //$NON-NLS-1$ |
| // return symbol; |
| // } |
| // /** |
| // * Sets the symbol to be defined only inside the given element |
| // * |
| // * @param symbol |
| // * @param slement |
| // */ |
| // public void setDefinitionRange(final IComponentVar symbol, |
| // final Element slement) |
| // { |
| // if (slement instanceof IndexedRegion) |
| // { |
| // final IndexedRegion defRegion = (IndexedRegion) slement; |
| // final int defOffset = defRegion.getStartOffset(); |
| // final int defLength = defRegion.getLength(); |
| // symbol.setDefinitionRange(defOffset, defLength); |
| // } |
| // } |
| |
| } |