/*******************************************************************************
* Copyright (c) 2007-2011 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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
*
* Contributor:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.seam.internal.core.el;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.text.BadLocationException;
import org.jboss.tools.common.el.core.model.ELExpression;
import org.jboss.tools.common.el.core.model.ELInstance;
import org.jboss.tools.common.el.core.model.ELInvocationExpression;
import org.jboss.tools.common.el.core.model.ELModel;
import org.jboss.tools.common.el.core.parser.ELParser;
import org.jboss.tools.common.el.core.parser.ELParserUtil;
import org.jboss.tools.common.el.core.resolver.ELResolution;
import org.jboss.tools.common.el.core.resolver.ELSegment;
import org.jboss.tools.common.el.core.resolver.JavaMemberELSegmentImpl;
import org.jboss.tools.common.el.core.resolver.TypeInfoCollector;
import org.jboss.tools.common.el.core.resolver.TypeInfoCollector.MemberInfo;
import org.jboss.tools.common.el.core.resolver.TypeInfoCollector.Type;
import org.jboss.tools.common.el.core.resolver.TypeInfoCollector.TypeInfo;
import org.jboss.tools.common.el.core.resolver.TypeInfoCollector.TypeMemberInfo;
import org.jboss.tools.common.java.IJavaSourceReference;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.util.EclipseResourceUtil;
import org.jboss.tools.seam.core.BijectedAttributeType;
import org.jboss.tools.seam.core.IBijectedAttribute;
import org.jboss.tools.seam.core.ISeamComponent;
import org.jboss.tools.seam.core.ISeamComponentMethod;
import org.jboss.tools.seam.core.ISeamContextShortVariable;
import org.jboss.tools.seam.core.ISeamContextVariable;
import org.jboss.tools.seam.core.ISeamElement;
import org.jboss.tools.seam.core.ISeamJavaComponentDeclaration;
import org.jboss.tools.seam.core.ISeamMessages;
import org.jboss.tools.seam.core.ISeamProject;
import org.jboss.tools.seam.core.ISeamXmlFactory;
import org.jboss.tools.seam.core.SeamComponentMethodType;
import org.jboss.tools.seam.core.SeamCorePlugin;
/**
* Utility class used to resolve Seam project variables and to get the methods/properties and their presentation strings from type
*
* @author Jeremy
*/
public class SeamExpressionResolver {
/**
* Returns Seam project variables which names start from specified value
*
* @param project
* @param scope
* @param name
* @return
*/
public static List<ISeamContextVariable> resolveVariables(ISeamProject project, IFile context, String name, boolean onlyEqualNames) {
if (project == null || name == null) return null;
ISeamProject parent = project.getParentProject();
if(parent != null) {
project = parent;
}
ISeamJavaComponentDeclaration declaration = null;
if(context != null) {
Set<ISeamComponent> cs = project.getComponentsByPath(context.getFullPath());
for (ISeamComponent c: cs) {
ISeamJavaComponentDeclaration d = c.getJavaDeclaration();
if(d != null && context.getFullPath().equals(d.getSourcePath())) {
declaration = d;
}
}
}
/*
* JBIDE-670 scope isn't used anymore
*/
// return (scope == null ? internalResolveVariables(project, name, onlyEqualNames) :
// internalResolveVariablesByScope(project, scope, name, onlyEqualNames));
return internalResolveVariables(project, declaration, name, onlyEqualNames);
}
/**
* Returns Seam project variables which names start from specified value
* No scope used
*
* @param project
* @param name
* @return
*/
private static List<ISeamContextVariable> internalResolveVariables(ISeamProject project, ISeamJavaComponentDeclaration context, String name, boolean onlyEqualNames) {
//TODO add variables from imported packages
Set<ISeamContextVariable> variables = project.getVariables(context);
return internalResolveVariables(project, context, name, onlyEqualNames, variables);
}
private static List<ISeamContextVariable> internalResolveVariables(ISeamProject project, ISeamJavaComponentDeclaration context, String name, boolean onlyEqualNames, Set<ISeamContextVariable> variables) {
List<ISeamContextVariable> resolvedVariables = new ArrayList<ISeamContextVariable>();
if(onlyEqualNames) {
if(context != null) {
variables = context.getVariablesByName(name);
} else variables = new HashSet<ISeamContextVariable>();
if(variables.isEmpty()) {
variables = project.getVariablesByName(name);
}
if(variables != null) resolvedVariables.addAll(variables);
return resolvedVariables;
}
for (ISeamContextVariable variable : variables) {
String n = variable.getName();
if (n.startsWith(name)) {
resolvedVariables.add(variable);
}
}
return resolvedVariables;
}
/**
* Returns the IMember for the variable specified
*
* @param variable
* @return
*/
public static IMember getMemberByVariable(ISeamContextVariable variable, boolean onlyEqualNames) {
IMember member = null;
if(variable instanceof ISeamContextShortVariable) {
return getMemberByVariable(((ISeamContextShortVariable)variable).getOriginal(), onlyEqualNames);
}
if (variable instanceof ISeamComponent) {
ISeamComponent component = (ISeamComponent)variable;
ISeamJavaComponentDeclaration decl = component.getJavaDeclaration();
if (decl != null) {
member = decl.getSourceMember();
}
}
if (member == null && variable instanceof IBijectedAttribute) {
member = ((IJavaSourceReference)variable).getSourceMember();
}
if (member == null && variable instanceof IJavaSourceReference) {
member = ((IJavaSourceReference)variable).getSourceMember();
}
if (member == null && variable instanceof ISeamXmlFactory) {
ISeamXmlFactory factory = (ISeamXmlFactory)variable;
String value = factory.getValue();
if (value != null && value.length() > 0) {
if (value.startsWith("#{") || value.startsWith("${")) //$NON-NLS-1$ //$NON-NLS-2$
value = value.substring(2);
if (value.endsWith("}")) //$NON-NLS-1$
value = value.substring(0, value.length() - 1);
}
if (value != null && value.length() > 0) {
// TODO: Need to make sure that it's correct way to get the project and
// the scope from the factory
ISeamProject project = ((ISeamElement)factory).getSeamProject();
// ISeamProject project = getSeamProject(factory.getResource());
if (project != null) {
List<ISeamContextVariable> resolvedValues = resolveVariables(project, null /* factory.getScope()*/, value, onlyEqualNames);
for (ISeamContextVariable var : resolvedValues) {
if (var.getName().equals(value)) {
member = getMemberByVariable(var, onlyEqualNames);
break;
}
}
}
}
}
return member;
}
/**
* This object wraps "messages" context variable.
* @author Alexey Kazakov
*/
public static class MessagesInfo extends TypeMemberInfo {
private ISeamMessages messages;
/**
* @param parentMember
* @param messages
* @throws JavaModelException
*/
protected MessagesInfo(MemberInfo parentMember, ISeamMessages messages) throws JavaModelException {
super(null, null, messages.getName(), 0, null, null, false, null);
this.messages = messages;
IMember member = (IMember)getJavaElement();
if(member!=null) {
IType type = member.getDeclaringType();
if(member instanceof IType) {
type = (IType)member;
}
if(type!=null) {
setSourceType(type);
setDeclaringTypeQualifiedName(type==null?null:type.getFullyQualifiedName());
setName(messages.getName());
setModifiers(type.getFlags());
setParentMember(parentMember);
if(parentMember == null || parentMember instanceof TypeMemberInfo) {
TypeInfo typeInfo = new TypeInfo(type, null, false);
setDeclaratedType(typeInfo);
} else {
setDeclaratedType((TypeInfo)parentMember);
}
setDataModel(false);
setType(type==null?null:new Type(null, type));
}
}
}
/*
* (non-Javadoc)
* @see org.jboss.tools.common.model.util.TypeInfoCollector.MemberInfo#getJavaElement()
*/
@Override
public IJavaElement getJavaElement() {
if(messages instanceof IJavaSourceReference) {
return ((IJavaSourceReference)messages).getSourceMember();
} else if(messages instanceof ISeamComponent) {
ISeamComponent c = (ISeamComponent)messages;
ISeamJavaComponentDeclaration d = c.getJavaDeclaration();
if(d != null) return d.getSourceMember();
}
return null;
}
/**
* @return property
*/
public ISeamMessages getMessages() {
return messages;
}
/**
* @return keys of resource bundle
*/
public Collection<String> getKeys() {
return messages.getPropertyNames();
}
/**
* Returns mapping of property name to all model objects that represent it,
* for all locales and all bundles.
*
* @return
*/
public Map<String, List<XModelObject>> getPropertiesMap() {
return messages.getPropertiesMap();
}
}
/**
* Returns the IMember for the variable specified
*
* @param variable
* @return
*/
public static TypeInfoCollector.MemberInfo getMemberInfoByVariable(ISeamContextVariable variable, boolean onlyEqualNames, SeamELCompletionEngine engine, int offset) {
TypeInfoCollector.MemberInfo member = null;
if(variable instanceof ISeamContextShortVariable) {
return getMemberInfoByVariable(((ISeamContextShortVariable)variable).getOriginal(), onlyEqualNames, engine, offset);
}
if(variable instanceof ISeamMessages) {
MemberInfo info = null;;
try {
info = new MessagesInfo(null, (ISeamMessages)variable);
} catch (JavaModelException e) {
SeamCorePlugin.getPluginLog().logError(e);
}
return info;
}
if (variable instanceof ISeamComponent) {
ISeamComponent component = (ISeamComponent)variable;
// Use UNWRAP method type instead of ISeamComponent type if it exists
IMember unwrapSourceMember = null;
for (ISeamComponentMethod method : component.getMethods()) {
if (method.getTypes()!= null && method.getTypes().contains(SeamComponentMethodType.UNWRAP) ) {
unwrapSourceMember = method.getSourceMember();
break;
}
}
if (unwrapSourceMember != null) {
member = TypeInfoCollector.createMemberInfo(unwrapSourceMember);
} else {
ISeamJavaComponentDeclaration decl = component.getJavaDeclaration();
if (decl != null) {
member = TypeInfoCollector.createMemberInfo(decl.getSourceMember());
} else {
// Maybe it is framework component? Then let's try to find the class of it.
String className = component.getClassName();
if(className!=null) {
IJavaProject project = EclipseResourceUtil.getJavaProject(component.getSeamProject().getProject());
try {
IType type = project.findType(className);
if(type!=null) {
member = TypeInfoCollector.createMemberInfo(type);
}
} catch (JavaModelException e) {
SeamCorePlugin.getDefault().logError(e);
}
}
}
}
}
if (member == null && variable instanceof IBijectedAttribute) {
boolean isDataModel = false;
BijectedAttributeType[] types = ((IBijectedAttribute)variable).getTypes();
for(int i=0; i<types.length; i++) {
if(types[i]==BijectedAttributeType.DATA_BINDER) {
isDataModel = true;
break;
}
}
member = TypeInfoCollector.createMemberInfo(((IJavaSourceReference)variable).getSourceMember(), isDataModel);
}
if (member == null && variable instanceof IJavaSourceReference) {
member = TypeInfoCollector.createMemberInfo(((IJavaSourceReference)variable).getSourceMember());
}
if (member == null && variable instanceof ISeamXmlFactory) {
ISeamXmlFactory factory = (ISeamXmlFactory)variable;
String value = factory.getValue();
if (value != null && value.length() > 0) {
if (value.startsWith("#{") || value.startsWith("${")) //$NON-NLS-1$ //$NON-NLS-2$
value = value.substring(2);
if (value.endsWith("}")) //$NON-NLS-1$
value = value.substring(0, value.length() - 1);
}
if (value != null && value.length() > 0) {
// TODO: Need to make sure that it's correct way to get the project and
// the scope from the factory
ISeamProject project = ((ISeamElement)factory).getSeamProject();
// ISeamProject project = getSeamProject(factory.getResource());
if (project != null) {
List<ISeamContextVariable> resolvedValues = resolveVariables(project, null /* factory.getScope()*/, value, onlyEqualNames);
for (ISeamContextVariable var : resolvedValues) {
if (var.getName().equals(value)) {
member = getMemberInfoByVariable(var, onlyEqualNames, engine, offset);
break;
}
}
}
}
if(member == null) {
ELParser p = ELParserUtil.getJbossFactory().createParser();
ELModel m = p.parse(factory.getValue());
ELInstance i = m.getInstances().isEmpty() ? null : m.getInstances().get(0);
ELExpression ex = i == null ? null : i.getExpression();
if(ex instanceof ELInvocationExpression) {
ELInvocationExpression expr = (ELInvocationExpression)ex;
try {
ELResolution resolution = engine.resolveEL(null, null, expr, false, offset);
if(resolution != null && resolution.isResolved()) {
ELSegment segment = resolution.getLastSegment();
if(segment instanceof JavaMemberELSegmentImpl) {
member = ((JavaMemberELSegmentImpl)segment).getMemberInfo();
}
}
} catch (StringIndexOutOfBoundsException e) {
SeamCorePlugin.getDefault().logError(e);
} catch (BadLocationException e) {
SeamCorePlugin.getDefault().logError(e);
}
}
}
}
return member;
}
}