@Override
  public List<ICompletionProposal> computeCompletionProposals(
      final ContentAssistInvocationContext context, final IProgressMonitor monitor) {
    final List<ICompletionProposal> list = new ArrayList<ICompletionProposal>();
    boolean extendContext = false;
    try {
      if (context instanceof JavaContentAssistInvocationContext) {
        final ITextViewer viewer = context.getViewer();
        final List<ScriptVariable> scriptVariables = getScriptVariables(viewer);
        if (scriptVariables.isEmpty()) {
          return list;
        }
        final CompletionContext coreContext =
            ((JavaContentAssistInvocationContext) context).getCoreContext();
        if (coreContext != null && !coreContext.isExtended()) {
          // must use reflection to set the fields
          ReflectionUtils.setPrivateField(
              InternalCompletionContext.class, "isExtended", coreContext, true);
          extendContext = true;
        }
        final ICompilationUnit unit =
            ((JavaContentAssistInvocationContext) context).getCompilationUnit();
        if (unit instanceof GroovyCompilationUnit) {
          if (((GroovyCompilationUnit) unit).getModuleNode() == null) {
            return Collections.emptyList();
          }
          final ContentAssistContext assistContext =
              new GroovyCompletionProposalComputer()
                  .createContentAssistContext(
                      (GroovyCompilationUnit) unit,
                      context.getInvocationOffset(),
                      context.getDocument());
          CharSequence prefix = null;
          try {
            prefix = context.computeIdentifierPrefix();
          } catch (final BadLocationException e) {
            BonitaStudioLog.error(e);
          }

          if (assistContext != null && assistContext.completionNode instanceof VariableExpression) {
            try {
              final VariableExpression expr = (VariableExpression) assistContext.completionNode;
              if (scriptVariables != null) {
                for (final ScriptVariable f : scriptVariables) {
                  if (expr.getName().equals(f.getName())) {
                    final IType type = javaProject.findType(f.getType());
                    if (type == null) {
                      return list;
                    }
                    for (final IMethod m : type.getMethods()) {
                      if (m.getElementName().startsWith(prefix.toString())) {
                        final GroovyCompletionProposal proposal =
                            new GroovyCompletionProposal(
                                CompletionProposal.METHOD_REF, context.getInvocationOffset());
                        proposal.setName(m.getElementName().toCharArray());
                        proposal.setCompletion(
                            m.getElementName().substring(prefix.length()).toCharArray());
                        proposal.setFlags(m.getFlags());

                        if (prefix.length() == m.getElementName().length()) {
                          proposal.setReplaceRange(
                              context.getInvocationOffset(), context.getInvocationOffset());
                          proposal.setReceiverRange(0, 0);
                        } else {
                          proposal.setReplaceRange(
                              context.getInvocationOffset() - prefix.length(),
                              context.getInvocationOffset());
                          proposal.setReceiverRange(prefix.length(), prefix.length());
                        }

                        final char[][] parametersArray =
                            new char[m.getParameterNames().length][256];
                        final List<Parameter> parameters = new ArrayList<Parameter>();
                        for (int i = 0; i < m.getParameterNames().length; i++) {
                          parametersArray[i] = m.getParameterNames()[i].toCharArray();
                          parameters.add(
                              new Parameter(
                                  ClassHelper.make(
                                      Signature.getSignatureSimpleName(m.getParameterTypes()[i])),
                                  m.getParameterNames()[i]));
                        }

                        final ClassNode classNode =
                            ClassHelper.make(m.getDeclaringType().getFullyQualifiedName());
                        proposal.setDeclarationSignature(
                            ProposalUtils.createTypeSignature(classNode));
                        proposal.setParameterNames(parametersArray);
                        if (m.getDeclaringType().getFullyQualifiedName().equals(f.getType())) {
                          proposal.setRelevance(100);
                        }

                        final MethodNode methodNode =
                            new MethodNode(
                                m.getElementName(),
                                m.getFlags(),
                                ClassHelper.make(
                                    Signature.getSignatureSimpleName(m.getReturnType())),
                                parameters.toArray(new Parameter[parameters.size()]),
                                new ClassNode[0],
                                null);
                        final char[] methodSignature =
                            ProposalUtils.createMethodSignature(methodNode);
                        proposal.setSignature(methodSignature);

                        final GroovyJavaGuessingCompletionProposal groovyProposal =
                            GroovyJavaGuessingCompletionProposal.createProposal(
                                proposal,
                                (JavaContentAssistInvocationContext) context,
                                true,
                                "Groovy",
                                ProposalFormattingOptions.newFromOptions());
                        if (groovyProposal != null) {
                          list.add(groovyProposal);
                        }
                      }
                    }
                  }
                }
              }
            } catch (final JavaModelException e) {
              BonitaStudioLog.error(e);
            }
          }
        }

        return list;
      }
    } finally {
      final CompletionContext coreContext =
          ((JavaContentAssistInvocationContext) context).getCoreContext();
      if (extendContext && coreContext != null && coreContext.isExtended()) {
        // must use reflection to set the fields
        ReflectionUtils.setPrivateField(
            InternalCompletionContext.class, "isExtended", coreContext, false);
      }
    }

    return Collections.emptyList();
  }
/**
 * @author andrew
 * @created Nov 17, 2010
 */
public class MethodContributionElement implements IContributionElement {

  private static final BlockStatement EMPTY_BLOCK = new BlockStatement();
  private static final ClassNode[] NO_EXCEPTIONS = new ClassNode[0];
  private static final Parameter[] NO_PARAMETERS = new Parameter[0];
  private static final ParameterContribution[] NO_PARAMETER_CONTRIBUTION =
      new ParameterContribution[0];
  private static final ClassNode UNKNOWN_TYPE = ClassHelper.DYNAMIC_TYPE;

  private final String methodName;
  private final ParameterContribution[] params;
  private final ParameterContribution[] namedParams;
  private final ParameterContribution[] optionalParams;
  private final String returnType;
  private final String declaringType;
  private final boolean isStatic;
  private final boolean useNamedArgs;

  private final String provider;
  private final String doc;

  private ClassNode cachedDeclaringType;
  private ClassNode cachedReturnType;
  private Parameter[] cachedRegularParameters;
  private Parameter[] cachedNamedParameters;
  private Parameter[] cachedOptionalParameters;
  private ProposalFormattingOptions options = ProposalFormattingOptions.newFromOptions();
  private final int relevanceMultiplier;
  private final boolean isDeprecated;

  public MethodContributionElement(
      String methodName,
      ParameterContribution[] params,
      String returnType,
      String declaringType,
      boolean isStatic,
      String provider,
      String doc,
      boolean useNamedArgs,
      boolean isDeprecated,
      int relevanceMultiplier) {
    this(
        methodName,
        params,
        NO_PARAMETER_CONTRIBUTION,
        NO_PARAMETER_CONTRIBUTION,
        returnType,
        declaringType,
        isStatic,
        provider,
        doc,
        useNamedArgs,
        isDeprecated,
        relevanceMultiplier);
  }

  public MethodContributionElement(
      String methodName,
      ParameterContribution[] params,
      ParameterContribution[] namedParams,
      ParameterContribution[] optionalParams,
      String returnType,
      String declaringType,
      boolean isStatic,
      String provider,
      String doc,
      boolean useNamedArgs,
      boolean isDeprecated,
      int relevanceMultiplier) {
    this.methodName = methodName;
    this.params = params;
    this.namedParams = namedParams;
    this.optionalParams = optionalParams;
    this.returnType = returnType;
    this.isStatic = isStatic;
    this.declaringType = declaringType;
    this.useNamedArgs = useNamedArgs;
    this.isDeprecated = isDeprecated;
    this.relevanceMultiplier = relevanceMultiplier;

    this.provider = provider == null ? GROOVY_DSL_PROVIDER : provider;
    this.doc = doc == null ? NO_DOC + this.provider : doc;
  }

  public TypeAndDeclaration lookupType(
      String name, ClassNode declaringType, ResolverCache resolver) {
    if (name.equals(methodName))
      return new TypeAndDeclaration(
          ensureReturnType(resolver),
          toMethod(declaringType, resolver),
          ensureDeclaringType(declaringType, resolver),
          doc);
    else return null;
  }

  public IGroovyProposal toProposal(ClassNode declaringType, ResolverCache resolver) {
    GroovyMethodProposal groovyMethodProposal =
        new GroovyMethodProposal(toMethod(declaringType.redirect(), resolver), provider, options);
    groovyMethodProposal.setUseNamedArguments(useNamedArgs);
    groovyMethodProposal.setRelevanceMultiplier(relevanceMultiplier);
    return groovyMethodProposal;
  }

  public List<IGroovyProposal> extraProposals(
      ClassNode declaringType, ResolverCache resolver, Expression expression) {
    // first find the arguments that are possible
    Map<String, ClassNode> availableParams = findAvailableParamNames(resolver);

    if (availableParams.isEmpty()) {
      return ProposalUtils.NO_PROPOSALS;
    }

    if (expression instanceof MethodCallExpression) {
      // next find out if there are any existing named args
      MethodCallExpression call = (MethodCallExpression) expression;
      Expression arguments = call.getArguments();
      if (arguments instanceof TupleExpression) {
        for (Expression maybeArg : ((TupleExpression) arguments).getExpressions()) {
          if (maybeArg instanceof MapExpression) {
            arguments = maybeArg;
            break;
          }
        }
      }

      // now remove the arguments that are already written
      if (arguments instanceof MapExpression) {
        // Do extra filtering to determine what parameters are still available
        MapExpression enclosingCallArgs = (MapExpression) arguments;
        for (MapEntryExpression entry : enclosingCallArgs.getMapEntryExpressions()) {
          String paramName = entry.getKeyExpression().getText();
          availableParams.remove(paramName);
        }
      }
    }

    List<IGroovyProposal> extraProposals = new ArrayList<IGroovyProposal>(availableParams.size());
    for (Entry<String, ClassNode> available : availableParams.entrySet()) {
      extraProposals.add(
          new GroovyNamedArgumentProposal(
              available.getKey(),
              available.getValue(),
              toMethod(declaringType.redirect(), resolver),
              provider));
    }
    return extraProposals;
  }

  /**
   * @param resolver
   * @return
   */
  private Map<String, ClassNode> findAvailableParamNames(ResolverCache resolver) {
    Map<String, ClassNode> available = new HashMap<String, ClassNode>(params.length);
    if (useNamedArgs) {
      for (ParameterContribution param : params) {
        available.put(param.name, param.toParameter(resolver).getType());
      }
    }

    for (ParameterContribution param : namedParams) {
      available.put(param.name, param.toParameter(resolver).getType());
    }

    for (ParameterContribution param : optionalParams) {
      available.put(param.name, param.toParameter(resolver).getType());
    }

    return available;
  }

  private MethodNode toMethod(ClassNode declaringType, ResolverCache resolver) {
    if (cachedRegularParameters == null) {
      cachedRegularParameters = initParams(params, resolver);
      cachedOptionalParameters = initParams(optionalParams, resolver);
      cachedNamedParameters = initParams(namedParams, resolver);
      if (cachedReturnType == null) {
        if (resolver != null) {
          cachedReturnType = resolver.resolve(returnType);
        } else {
          cachedReturnType = VariableScope.OBJECT_CLASS_NODE;
        }
      }
    }
    MethodNode meth =
        new NamedArgsMethodNode(
            methodName,
            opcode(),
            cachedReturnType,
            cachedRegularParameters,
            cachedNamedParameters,
            cachedOptionalParameters,
            NO_EXCEPTIONS,
            EMPTY_BLOCK);
    meth.setDeclaringClass(ensureDeclaringType(declaringType, resolver));
    return meth;
  }

  private Parameter[] initParams(ParameterContribution[] pcs, ResolverCache resolver) {
    Parameter[] ps;
    if (pcs == null) {
      ps = NO_PARAMETERS;
    } else {
      ps = new Parameter[pcs.length];
      for (int i = 0; i < pcs.length; i++) {
        ps[i] = pcs[i].toParameter(resolver);
      }
    }
    return ps;
  }

  protected ClassNode ensureReturnType(ResolverCache resolver) {
    if (cachedReturnType == null) {
      cachedReturnType = resolver.resolve(returnType);
    }
    return cachedReturnType == null ? UNKNOWN_TYPE : cachedReturnType;
  }

  protected ClassNode ensureDeclaringType(ClassNode lexicalDeclaringType, ResolverCache resolver) {
    if (declaringType != null && cachedDeclaringType == null) {
      cachedDeclaringType = resolver.resolve(declaringType);
    }
    return cachedDeclaringType == null ? lexicalDeclaringType : cachedDeclaringType;
  }

  protected int opcode() {
    int modifiers = isStatic ? Opcodes.ACC_STATIC : Opcodes.ACC_PUBLIC;
    modifiers |= isDeprecated ? Opcodes.ACC_DEPRECATED : 0;
    return modifiers;
  }

  public String contributionName() {
    return methodName;
  }

  public String description() {
    return "Method: " + declaringType + "." + methodName + "(..)";
  }

  public String getDeclaringTypeName() {
    return declaringType;
  }

  @Override
  public String toString() {
    return "public "
        + (isStatic ? "static " : "")
        + (isDeprecated ? "deprecated " : "")
        + (useNamedArgs ? "useNamedArgs " : "")
        + returnType
        + " "
        + declaringType
        + "."
        + methodName
        + "("
        + Arrays.toString(params)
        + ") ("
        + provider
        + ")";
  }
}