com.google.gwt.eclipse.core.wizards.rpc.NewAsyncRemoteServiceInterfaceCreationWizardPage.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gwt.eclipse.core.wizards.rpc.NewAsyncRemoteServiceInterfaceCreationWizardPage.java

Source

/*******************************************************************************
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package com.google.gwt.eclipse.core.wizards.rpc;

import com.google.gdt.eclipse.core.JavaASTUtils;
import com.google.gdt.eclipse.core.StringUtilities;
import com.google.gwt.eclipse.core.GWTPlugin;
import com.google.gwt.eclipse.core.resources.GWTImages;
import com.google.gwt.eclipse.core.validators.rpc.RemoteServiceUtilities;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.IStringButtonAdapter;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.LayoutUtil;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.StringButtonDialogField;
import org.eclipse.jdt.ui.wizards.NewInterfaceWizardPage;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;

import java.util.ArrayList;
import java.util.List;

/**
 * Modifications to the {@link NewInterfaceWizardPage} to enable us to add the
 * necessary type members to the asynchronous remote service interface.
 * 
 * TODO: We might be able to use an AST based approach here. If so, we could
 * unify this with the individual method quick fixes.
 * 
 * NOTE: This class assumes that the package, enclosing type and sync type
 * fields cannot be edited. Additional work will be required to change this
 * invariant.
 */
@SuppressWarnings("restriction")
public class NewAsyncRemoteServiceInterfaceCreationWizardPage extends NewInterfaceWizardPage {
    /**
     * Adapter that allows us to reuse the method generation code for previews
     * when there isn't a real import manager.
     */
    public interface ImportManagerAdapter {
        String addImport(ITypeBinding typeBinding);

        String addImport(String qualifiedTypeName);
    }

    private static final String[] NO_STRINGS = new String[0];

    public static List<IMethodBinding> computeSyncMethodsThatNeedAsyncVersions(ITypeBinding syncTypeBinding,
            ITypeBinding asyncTypeBinding) {
        // Compute all overridable methods on the sync interface
        List<IMethodBinding> overridableSyncMethods = computeOverridableMethodsForInterface(syncTypeBinding);

        // Remove sync methods that would override existing methods in the
        // async hierarchy
        List<IMethodBinding> remainingMethods = new ArrayList<IMethodBinding>();
        for (IMethodBinding overridableSyncMethod : overridableSyncMethods) {
            IMethod syncMethod = (IMethod) overridableSyncMethod.getJavaElement();
            String[] asyncParameterTypes = RemoteServiceUtilities.computeAsyncParameterTypes(overridableSyncMethod);
            if (Bindings.findMethodInHierarchy(asyncTypeBinding, syncMethod.getElementName(),
                    asyncParameterTypes) != null) {
                // Don't add method that appear in the type hierarchy
                continue;
            }

            remainingMethods.add(overridableSyncMethod);
        }

        return remainingMethods;
    }

    public static String createMethodContents(IType newType, ImportManagerAdapter imports,
            IMethodBinding overridableSyncMethod, boolean addComments) throws CoreException, JavaModelException {
        StringBuilder sb = new StringBuilder();

        IMethod syncMethod = (IMethod) overridableSyncMethod.getJavaElement();

        if (addComments) {
            String lineDelimiter = "\n"; // OK, since content is formatted afterwards

            // Don't go through CodeGeneration type since it can't deal with delegates
            String comment = StubUtility.getMethodComment(newType.getCompilationUnit(),
                    newType.getFullyQualifiedName(), syncMethod.getElementName(), NO_STRINGS, NO_STRINGS,
                    Signature.SIG_VOID, NO_STRINGS, syncMethod, true, lineDelimiter);
            if (comment != null) {
                sb.append(comment);
                sb.append(lineDelimiter);
            }
        }

        // Expand the type parameters
        ITypeParameter[] typeParameters = syncMethod.getTypeParameters();
        ITypeBinding[] typeParameterBindings = overridableSyncMethod.getTypeParameters();
        if (typeParameters.length > 0) {
            sb.append("<");
            for (int i = 0; i < typeParameters.length; ++i) {
                sb.append(typeParameters[i].getElementName());
                ITypeBinding typeParameterBinding = typeParameterBindings[i];
                ITypeBinding[] typeBounds = typeParameterBinding.getTypeBounds();
                if (typeBounds.length > 0) {
                    sb.append(" extends ");
                    for (int j = 0; j < typeBounds.length; ++j) {
                        if (j != 0) {
                            sb.append(" & ");
                        }
                        expandTypeBinding(typeBounds[j], sb, imports);
                    }
                }
            }
            sb.append(">");
        }

        // Default to a void return type for the async method
        sb.append("void ");

        // Expand the method name
        sb.append(overridableSyncMethod.getName());

        // Expand the arguments
        sb.append("(");
        String[] parameterNames = syncMethod.getParameterNames();
        ITypeBinding[] parameterTypes = overridableSyncMethod.getParameterTypes();
        for (int i = 0; i < parameterNames.length; ++i) {
            if (i != 0) {
                sb.append(", ");
            }

            expandTypeBinding(parameterTypes[i], sb, imports);
            sb.append(" ");
            sb.append(parameterNames[i]);
        }

        if (parameterNames.length > 0) {
            sb.append(", ");
        }

        sb.append(imports.addImport(RemoteServiceUtilities.ASYNCCALLBACK_QUALIFIED_NAME));
        sb.append("<");
        ITypeBinding syncReturnType = overridableSyncMethod.getReturnType();
        if (syncReturnType.isPrimitive()) {
            String wrapperTypeName = JavaASTUtils.getWrapperTypeName(syncReturnType.getName());
            sb.append(imports.addImport(wrapperTypeName));
        } else {
            expandTypeBinding(syncReturnType, sb, imports);
        }
        sb.append("> ");
        sb.append(StringUtilities.computeUniqueName(parameterNames, "callback"));
        sb.append(");");

        return sb.toString();
    }

    static void expandTypeBinding(ITypeBinding typeBinding, StringBuilder sb, ImportManagerAdapter importsManager) {

        if (typeBinding.isParameterizedType()) {
            expandTypeBinding(typeBinding.getErasure(), sb, importsManager);

            sb.append("<");
            ITypeBinding[] typeArguments = typeBinding.getTypeArguments();
            for (int i = 0; i < typeArguments.length; ++i) {
                if (i != 0) {
                    sb.append(", ");
                }
                expandTypeBinding(typeArguments[i], sb, importsManager);
            }

            sb.append(">");
        } else if (typeBinding.isWildcardType()) {
            ITypeBinding bound = typeBinding.getBound();
            if (bound == null) {
                sb.append("?");
            } else {
                if (typeBinding.isUpperbound()) {
                    sb.append("? extends ");
                } else {
                    sb.append("? super ");
                }
                expandTypeBinding(bound, sb, importsManager);
            }
        } else if (typeBinding.isTypeVariable()) {
            sb.append(typeBinding.getName());
        } else if (typeBinding.isArray()) {
            expandTypeBinding(typeBinding.getComponentType(), sb, importsManager);
            sb.append("[]");
        } else if (typeBinding.isPrimitive()) {
            sb.append(typeBinding.getName());
        } else {
            sb.append(importsManager.addImport(typeBinding));
        }
    }

    private static List<IMethodBinding> computeOverridableMethodsForInterface(ITypeBinding interfaceBinding) {
        assert (interfaceBinding.isInterface());

        List<ITypeBinding> superInterfaces = new ArrayList<ITypeBinding>();
        RemoteServiceUtilities.expandSuperInterfaces(interfaceBinding, superInterfaces);

        List<IMethodBinding> overridableMethods = new ArrayList<IMethodBinding>();
        for (ITypeBinding superInterface : superInterfaces) {
            for (IMethodBinding declaredMethod : superInterface.getDeclaredMethods()) {
                if (findOverridingMethod(declaredMethod, overridableMethods) == null) {
                    overridableMethods.add(declaredMethod);
                }
            }
        }

        return overridableMethods;
    }

    /**
     * NOTE: This method comes from StubUtility2.
     */
    @SuppressWarnings("deprecation")
    private static IMethodBinding findOverridingMethod(IMethodBinding method, List<IMethodBinding> allMethods) {
        for (IMethodBinding cur : allMethods) {
            if (Bindings.areOverriddenMethods(cur, method) || Bindings.isSubsignature(cur, method)) {
                return cur;
            }
        }
        return null;
    }

    private final ITypeBinding syncTypeBinding;
    private final StringButtonDialogField syncTypeDialogField;

    public NewAsyncRemoteServiceInterfaceCreationWizardPage(ITypeBinding syncTypeBinding) {
        super();
        this.syncTypeBinding = syncTypeBinding;
        syncTypeDialogField = new StringButtonDialogField(new IStringButtonAdapter() {
            public void changeControlPressed(DialogField field) {
                // Purposely ignored
            }
        });
        syncTypeDialogField.setButtonLabel("Browse...");
        syncTypeDialogField.setLabelText("Synchronous type:");
        syncTypeDialogField.setEnabled(false);
        syncTypeDialogField.setText(syncTypeBinding.getQualifiedName());
        ImageDescriptor imageDescriptor = GWTPlugin.getDefault().getImageRegistry()
                .getDescriptor(GWTImages.NEW_ASYNC_INTERFACE_LARGE);
        setImageDescriptor(imageDescriptor);
        setDescription("Create a new asynchronous remote service interface");
    }

    @Override
    public void createControl(Composite parent) {
        initializeDialogUnits(parent);

        Composite composite = new Composite(parent, SWT.NONE);
        composite.setFont(parent.getFont());

        int nColumns = 4;

        GridLayout layout = new GridLayout();
        layout.numColumns = nColumns;
        composite.setLayout(layout);

        createContainerControls(composite, nColumns);
        createPackageControls(composite, nColumns);
        createEnclosingTypeControls(composite, nColumns);
        createSyncTypeControls(composite, nColumns);

        createSeparator(composite, nColumns);

        createTypeNameControls(composite, nColumns);
        createModifierControls(composite, nColumns);

        createSuperInterfacesControls(composite, nColumns);

        createCommentControls(composite, nColumns);
        enableCommentControl(true);

        setControl(composite);

        Dialog.applyDialogFont(composite);
        PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, IJavaHelpContextIds.NEW_INTERFACE_WIZARD_PAGE);
    }

    @Override
    protected void createTypeMembers(IType newType, final ImportsManager imports, IProgressMonitor monitor)
            throws CoreException {
        TypeDeclaration asyncTypeDeclaration = JavaASTUtils.findTypeDeclaration(newType.getJavaProject(),
                newType.getFullyQualifiedName('.'));
        ITypeBinding asyncTypeBinding = asyncTypeDeclaration.resolveBinding();

        ImportManagerAdapter importAdapter = new ImportManagerAdapter() {

            public String addImport(ITypeBinding typeBinding) {
                return imports.addImport(typeBinding);
            }

            public String addImport(String qualifiedTypeName) {
                return imports.addImport(qualifiedTypeName);
            }
        };

        List<IMethodBinding> syncMethodsToConvert = computeSyncMethodsThatNeedAsyncVersions(syncTypeBinding,
                asyncTypeBinding);
        for (IMethodBinding overridableSyncMethod : syncMethodsToConvert) {
            String methodContents = createMethodContents(newType, importAdapter, overridableSyncMethod,
                    isAddComments());

            // Create the new method
            newType.createMethod(methodContents, null, false, monitor);
        }
    }

    /**
     * Creates the controls for the sync type name field. Expects a
     * <code>GridLayout</code> with at least 4 columns.
     * 
     * @param composite the parent composite
     * @param columns number of columns to span
     */
    private void createSyncTypeControls(Composite composite, int columns) {
        syncTypeDialogField.doFillIntoGrid(composite, columns);
        Text text = syncTypeDialogField.getTextControl(null);
        LayoutUtil.setWidthHint(text, getMaxFieldWidth());
    }
}