xtext / auto-gwt

A set of Active Annotations for GWT.
http://www.auto-gwt.org
Eclipse Public License 1.0
18 stars 10 forks source link

GwtService annotation #10

Closed pablowatson closed 9 years ago

pablowatson commented 9 years ago

Would it be, or is it possible to set the RemoteServiceRelativePath on the generated Async RPC interface to something other than a camel cased version of the classname i.e. something like.

@GwtService(remoteServiceRelativePath = 'somethingelse/servicename')

svenefftinge commented 9 years ago

Yes, that's generally possible. Currently it just uses the classes name making the first character lower case. The relevant code is

https://github.com/xtext/auto-gwt/blob/master/src/main/java/de/itemis/xtend/auto/gwt/GwtService.xtend#L68

pablowatson commented 9 years ago

Hi,

I made the changes to the GwtService annotation to accept an optional parameter remoteServiceRelativePath. Here is the modified class. Alternatively I could become a committer and push the modifications.

Cheers, Paul

/*******************************************************************************
 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
 * 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
 *******************************************************************************/
package de.itemis.xtend.auto.gwt

import com.google.gwt.user.client.rpc.AsyncCallback
import com.google.gwt.user.client.rpc.RemoteService
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath
import com.google.gwt.user.server.rpc.RemoteServiceServlet
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.Target
import org.eclipse.xtend.lib.macro.AbstractClassProcessor
import org.eclipse.xtend.lib.macro.Active
import org.eclipse.xtend.lib.macro.RegisterGlobalsContext
import org.eclipse.xtend.lib.macro.TransformationContext
import org.eclipse.xtend.lib.macro.declaration.ClassDeclaration
import org.eclipse.xtend.lib.macro.declaration.MutableClassDeclaration
import org.eclipse.xtend.lib.macro.declaration.Visibility

/**
 * Derives the server side and client side interfaces needed for GWT RPC.
 * Implements the server side interface.
 */
@Target(ElementType.TYPE)
@Active(GwtServiceProcessor)
@Retention(SOURCE)
annotation GwtService {
    String remoteServiceRelativePath = ''
}

class GwtServiceProcessor extends AbstractClassProcessor {

    private static final String IMPL = "Impl"

    private static final String SERVER = ".server"

    private static final String SHARED = ".shared"

    override doRegisterGlobals(ClassDeclaration it, RegisterGlobalsContext context) {
        context.registerInterface(interfaceName)
        context.registerInterface(interfaceAsyncName)
    }

    override doTransform(MutableClassDeclaration it, extension TransformationContext context) {
        if (!simpleName.endsWith(IMPL)) {
            addError('''The name must end with '«IMPL»'.''')
        }

        if (!packageName.contains(SERVER)) {
            addError("A service must reside under the 'server' package.")
        }

        if (extendedClass != Object.newTypeReference) {
            addError("A service must not extend another class.")
        }

        val interfaceType = findInterface(interfaceName)
        interfaceType.primarySourceElement = primarySourceElement
        val interfaceAsyncType = findInterface(interfaceAsyncName)
        interfaceAsyncType.primarySourceElement = primarySourceElement

        interfaceType.extendedInterfaces = interfaceType.extendedInterfaces + #[RemoteService.newTypeReference]

        val name = getRemoteServiceRelativePath(it, context)
        interfaceType.addAnnotation(
            RemoteServiceRelativePath.newAnnotationReference [
                set('value', name)
            ]
        )

        for (method : declaredMethods.filter[visibility == Visibility::PUBLIC]) {
            interfaceType.addMethod(method.simpleName) [
                returnType = method.returnType
                method.parameters.forEach(p|addParameter(p.simpleName, p.type))
                primarySourceElement = method.primarySourceElement
            ]
            interfaceAsyncType.addMethod(method.simpleName) [
                method.parameters.forEach(p|addParameter(p.simpleName, p.type))
                addParameter(
                    'result',
                    AsyncCallback.newTypeReference(method.returnType.wrapperIfPrimitive)
                )
                primarySourceElement = method.primarySourceElement
            ]
        }

        extendedClass = RemoteServiceServlet.newTypeReference
        implementedInterfaces = implementedInterfaces + #[interfaceType.newTypeReference]
    }

    def getRemoteServiceRelativePath(MutableClassDeclaration it, extension TransformationContext context) {
        val gwtServiceAnnotation = findAnnotation(GwtService.findTypeGlobally)
        val remoteServiceRelativePath = gwtServiceAnnotation.getValue('remoteServiceRelativePath') as String
        if (remoteServiceRelativePath.nullOrEmpty) {
            return interfaceSimpleName.toFirstLower
        }
        return remoteServiceRelativePath
    }

    def static interfaceAsyncName(ClassDeclaration it) {
        interfaceName + 'Async'
    }

    def static interfaceName(ClassDeclaration it) {
        packageName.replace(SERVER, SHARED) + "." + interfaceSimpleName
    }

    def static interfaceSimpleName(ClassDeclaration it) {
        simpleName.substring(0, simpleName.length - IMPL.length)
    }

    def static String packageName(ClassDeclaration it) {
        qualifiedName.substring(0, qualifiedName.length - simpleName.length - 1)
    }

}```
svenefftinge commented 9 years ago

Hi Paul, could you please fork the repository do the change and then just do a pull-request? Thanks, Sven

pablowatson commented 9 years ago

Done.