1/*
2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.tools.internal.ws.processor.modeler.annotation;
27
28import com.sun.tools.internal.ws.processor.model.Port;
29import com.sun.tools.internal.ws.resources.WebserviceapMessages;
30import com.sun.tools.internal.ws.util.ClassNameInfo;
31import com.sun.tools.internal.ws.wsdl.document.soap.SOAPStyle;
32import com.sun.xml.internal.ws.model.RuntimeModeler;
33
34import javax.jws.Oneway;
35import javax.jws.WebMethod;
36import javax.jws.WebParam;
37import javax.jws.WebResult;
38import javax.jws.WebService;
39import javax.jws.soap.SOAPBinding;
40import javax.jws.soap.SOAPBinding.ParameterStyle;
41import javax.lang.model.element.Element;
42import javax.lang.model.element.ElementKind;
43import javax.lang.model.element.ExecutableElement;
44import javax.lang.model.element.Modifier;
45import javax.lang.model.element.Name;
46import javax.lang.model.element.PackageElement;
47import javax.lang.model.element.TypeElement;
48import javax.lang.model.element.VariableElement;
49import javax.lang.model.type.DeclaredType;
50import javax.lang.model.type.NoType;
51import javax.lang.model.type.TypeKind;
52import javax.lang.model.type.TypeMirror;
53import javax.lang.model.util.ElementFilter;
54import javax.lang.model.util.SimpleElementVisitor6;
55import javax.lang.model.util.SimpleTypeVisitor6;
56import javax.lang.model.util.Types;
57import java.lang.annotation.Annotation;
58import java.util.Collection;
59import java.util.HashSet;
60import java.util.List;
61import java.util.Set;
62import java.util.Stack;
63
64/**
65 * @author WS Development Team
66 */
67public abstract class WebServiceVisitor extends SimpleElementVisitor6<Void, Object> {
68
69    protected ModelBuilder builder;
70    protected String wsdlNamespace;
71    protected String typeNamespace;
72    protected Stack<SOAPBinding> soapBindingStack;
73    protected SOAPBinding typeElementSoapBinding;
74    protected SOAPStyle soapStyle = SOAPStyle.DOCUMENT;
75    protected boolean wrapped = true;
76    protected Port port;
77    protected Name serviceImplName;
78    protected Name endpointInterfaceName;
79    protected AnnotationProcessorContext context;
80    protected AnnotationProcessorContext.SeiContext seiContext;
81    protected boolean processingSei = false;
82    protected String serviceName;
83    protected Name packageName;
84    protected String portName;
85    protected boolean endpointReferencesInterface = false;
86    protected boolean hasWebMethods = false;
87    protected TypeElement typeElement;
88    protected Set<String> processedMethods;
89    protected boolean pushedSoapBinding = false;
90
91    private static final NoTypeVisitor NO_TYPE_VISITOR = new NoTypeVisitor();
92
93    public WebServiceVisitor(ModelBuilder builder, AnnotationProcessorContext context) {
94        this.builder = builder;
95        this.context = context;
96        soapBindingStack = new Stack<SOAPBinding>();
97        processedMethods = new HashSet<String>();
98    }
99
100    @Override
101    public Void visitType(TypeElement e, Object o) {
102        WebService webService = e.getAnnotation(WebService.class);
103        if (!shouldProcessWebService(webService, e))
104            return null;
105        if (builder.checkAndSetProcessed(e))
106            return null;
107        typeElement = e;
108
109        switch (e.getKind()) {
110            case INTERFACE: {
111                if (endpointInterfaceName != null && !endpointInterfaceName.equals(e.getQualifiedName())) {
112                    builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTERFACES_DO_NOT_MATCH(endpointInterfaceName, e.getQualifiedName()), e);
113                }
114                verifySeiAnnotations(webService, e);
115                endpointInterfaceName = e.getQualifiedName();
116                processingSei = true;
117                preProcessWebService(webService, e);
118                processWebService(webService, e);
119                postProcessWebService(webService, e);
120                break;
121            }
122            case CLASS: {
123                typeElementSoapBinding = e.getAnnotation(SOAPBinding.class);
124                if (serviceImplName == null)
125                    serviceImplName = e.getQualifiedName();
126                String endpointInterfaceName = webService != null ? webService.endpointInterface() : null;
127                if (endpointInterfaceName != null && endpointInterfaceName.length() > 0) {
128                    checkForInvalidImplAnnotation(e, SOAPBinding.class);
129                    if (webService.name().length() > 0)
130                        builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTEFACE_PLUS_ELEMENT("name"), e);
131                    endpointReferencesInterface = true;
132                    verifyImplAnnotations(e);
133                    inspectEndpointInterface(endpointInterfaceName, e);
134                    serviceImplName = null;
135                    return null;
136                }
137                processingSei = false;
138                preProcessWebService(webService, e);
139                processWebService(webService, e);
140                serviceImplName = null;
141                postProcessWebService(webService, e);
142                serviceImplName = null;
143                break;
144            }
145            default:
146                break;
147        }
148        return null;
149    }
150
151    protected void verifySeiAnnotations(WebService webService, TypeElement d) {
152        if (webService.endpointInterface().length() > 0) {
153            builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTERFACE_ON_INTERFACE(
154                    d.getQualifiedName(), webService.endpointInterface()), d);
155        }
156        if (webService.serviceName().length() > 0) {
157            builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SEI_ANNOTATION_ELEMENT(
158                    "serviceName", d.getQualifiedName()), d);
159        }
160        if (webService.portName().length() > 0) {
161            builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SEI_ANNOTATION_ELEMENT(
162                    "portName", d.getQualifiedName()), d);
163        }
164    }
165
166    protected void verifyImplAnnotations(TypeElement d) {
167        for (ExecutableElement method : ElementFilter.methodsIn(d.getEnclosedElements())) {
168            checkForInvalidImplAnnotation(method, WebMethod.class);
169            checkForInvalidImplAnnotation(method, Oneway.class);
170            checkForInvalidImplAnnotation(method, WebResult.class);
171            for (VariableElement param : method.getParameters()) {
172                checkForInvalidImplAnnotation(param, WebParam.class);
173            }
174        }
175    }
176
177    protected void checkForInvalidSeiAnnotation(TypeElement element, Class annotationClass) {
178        Object annotation = element.getAnnotation(annotationClass);
179        if (annotation != null) {
180            builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SEI_ANNOTATION(
181                    annotationClass.getName(), element.getQualifiedName()), element);
182        }
183    }
184
185    protected void checkForInvalidImplAnnotation(Element element, Class annotationClass) {
186        Object annotation = element.getAnnotation(annotationClass);
187        if (annotation != null) {
188            builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTEFACE_PLUS_ANNOTATION(annotationClass.getName()), element);
189        }
190    }
191
192    protected void preProcessWebService(WebService webService, TypeElement element) {
193        processedMethods = new HashSet<String>();
194        seiContext = context.getSeiContext(element);
195        String targetNamespace = null;
196        if (webService != null)
197            targetNamespace = webService.targetNamespace();
198        PackageElement packageElement = builder.getProcessingEnvironment().getElementUtils().getPackageOf(element);
199        if (targetNamespace == null || targetNamespace.length() == 0) {
200            String packageName = packageElement.getQualifiedName().toString();
201            if (packageName == null || packageName.length() == 0) {
202                builder.processError(WebserviceapMessages.WEBSERVICEAP_NO_PACKAGE_CLASS_MUST_HAVE_TARGETNAMESPACE(
203                        element.getQualifiedName()), element);
204            }
205            targetNamespace = RuntimeModeler.getNamespace(packageName);
206        }
207        seiContext.setNamespaceUri(targetNamespace);
208        if (serviceImplName == null)
209            serviceImplName = seiContext.getSeiImplName();
210        if (serviceImplName != null) {
211            seiContext.setSeiImplName(serviceImplName);
212            context.addSeiContext(serviceImplName, seiContext);
213        }
214        portName = ClassNameInfo.getName(element.getSimpleName().toString().replace('$', '_'));
215        packageName = packageElement.getQualifiedName();
216        portName = webService != null && webService.name() != null && webService.name().length() > 0 ?
217                webService.name() : portName;
218        serviceName = ClassNameInfo.getName(element.getQualifiedName().toString()) + WebServiceConstants.SERVICE.getValue();
219        serviceName = webService != null && webService.serviceName() != null && webService.serviceName().length() > 0 ?
220                webService.serviceName() : serviceName;
221        wsdlNamespace = seiContext.getNamespaceUri();
222        typeNamespace = wsdlNamespace;
223
224        SOAPBinding soapBinding = element.getAnnotation(SOAPBinding.class);
225        if (soapBinding != null) {
226            pushedSoapBinding = pushSoapBinding(soapBinding, element, element);
227        } else if (element.equals(typeElement)) {
228            pushedSoapBinding = pushSoapBinding(new MySoapBinding(), element, element);
229        }
230    }
231
232    public static boolean sameStyle(SOAPBinding.Style style, SOAPStyle soapStyle) {
233        return style.equals(SOAPBinding.Style.DOCUMENT)
234                && soapStyle.equals(SOAPStyle.DOCUMENT)
235                || style.equals(SOAPBinding.Style.RPC)
236                && soapStyle.equals(SOAPStyle.RPC);
237    }
238
239    protected boolean pushSoapBinding(SOAPBinding soapBinding, Element bindingElement, TypeElement classElement) {
240        boolean changed = false;
241        if (!sameStyle(soapBinding.style(), soapStyle)) {
242            changed = true;
243            if (pushedSoapBinding)
244                builder.processError(WebserviceapMessages.WEBSERVICEAP_MIXED_BINDING_STYLE(
245                        classElement.getQualifiedName()), bindingElement);
246        }
247        if (soapBinding.style().equals(SOAPBinding.Style.RPC)) {
248            soapStyle = SOAPStyle.RPC;
249            wrapped = true;
250            if (soapBinding.parameterStyle().equals(ParameterStyle.BARE)) {
251                builder.processError(WebserviceapMessages.WEBSERVICEAP_RPC_LITERAL_MUST_NOT_BE_BARE(
252                        classElement.getQualifiedName()), bindingElement);
253            }
254        } else {
255            soapStyle = SOAPStyle.DOCUMENT;
256            if (wrapped != soapBinding.parameterStyle().equals(ParameterStyle.WRAPPED)) {
257                wrapped = soapBinding.parameterStyle().equals(ParameterStyle.WRAPPED);
258                changed = true;
259            }
260        }
261        if (soapBinding.use().equals(SOAPBinding.Use.ENCODED)) {
262            String style = "rpc";
263            if (soapBinding.style().equals(SOAPBinding.Style.DOCUMENT))
264                style = "document";
265            builder.processError(WebserviceapMessages.WEBSERVICE_ENCODED_NOT_SUPPORTED(
266                    classElement.getQualifiedName(), style), bindingElement);
267        }
268        if (changed || soapBindingStack.empty()) {
269            soapBindingStack.push(soapBinding);
270            pushedSoapBinding = true;
271        }
272        return changed;
273    }
274
275    protected SOAPBinding popSoapBinding() {
276        if (pushedSoapBinding)
277            soapBindingStack.pop();
278        SOAPBinding soapBinding = null;
279        if (!soapBindingStack.empty()) {
280            soapBinding = soapBindingStack.peek();
281            if (soapBinding.style().equals(SOAPBinding.Style.RPC)) {
282                soapStyle = SOAPStyle.RPC;
283                wrapped = true;
284            } else {
285                soapStyle = SOAPStyle.DOCUMENT;
286                wrapped = soapBinding.parameterStyle().equals(ParameterStyle.WRAPPED);
287            }
288        } else {
289                pushedSoapBinding = false;
290        }
291        return soapBinding;
292    }
293
294    protected String getNamespace(PackageElement packageElement) {
295        return RuntimeModeler.getNamespace(packageElement.getQualifiedName().toString());
296    }
297
298    protected boolean shouldProcessWebService(WebService webService, TypeElement element) {
299        switch (element.getKind()) {
300            case INTERFACE: {
301                hasWebMethods = false;
302                if (webService == null)
303                    builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTERFACE_HAS_NO_WEBSERVICE_ANNOTATION(
304                            element.getQualifiedName()), element);
305
306                SOAPBinding soapBinding = element.getAnnotation(SOAPBinding.class);
307                if (soapBinding != null
308                        && soapBinding.style() == SOAPBinding.Style.RPC
309                        && soapBinding.parameterStyle() == SOAPBinding.ParameterStyle.BARE) {
310                    builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SOAPBINDING_PARAMETERSTYLE(
311                            soapBinding, element), element);
312                    return false;
313                }
314                return isLegalSei(element);
315            }
316            case CLASS: {
317                if (webService == null)
318                    return false;
319                hasWebMethods = hasWebMethods(element);
320                SOAPBinding soapBinding = element.getAnnotation(SOAPBinding.class);
321                if (soapBinding != null
322                        && soapBinding.style() == SOAPBinding.Style.RPC
323                        && soapBinding.parameterStyle() == SOAPBinding.ParameterStyle.BARE) {
324                    builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SOAPBINDING_PARAMETERSTYLE(
325                            soapBinding, element), element);
326                    return false;
327                }
328                return isLegalImplementation(webService, element);
329            }
330            default: {
331                throw new IllegalArgumentException("Class or Interface was expecting. But element: " + element);
332            }
333        }
334    }
335
336    abstract protected void processWebService(WebService webService, TypeElement element);
337
338    protected void postProcessWebService(WebService webService, TypeElement element) {
339        processMethods(element);
340        popSoapBinding();
341    }
342
343    protected boolean hasWebMethods(TypeElement element) {
344        if (element.getQualifiedName().toString().equals(Object.class.getName()))
345            return false;
346        WebMethod webMethod;
347        for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
348            webMethod = method.getAnnotation(WebMethod.class);
349            if (webMethod != null) {
350                if (webMethod.exclude()) {
351                    if (webMethod.operationName().length() > 0)
352                        builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_WEBMETHOD_ELEMENT_WITH_EXCLUDE(
353                                "operationName", element.getQualifiedName(), method.toString()), method);
354                    if (webMethod.action().length() > 0)
355                        builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_WEBMETHOD_ELEMENT_WITH_EXCLUDE(
356                                "action", element.getQualifiedName(), method.toString()), method);
357                } else {
358                    return true;
359                }
360            }
361        }
362        return false;//hasWebMethods(d.getSuperclass().getDeclaration());
363    }
364
365    protected void processMethods(TypeElement element) {
366        switch (element.getKind()) {
367            case INTERFACE: {
368                builder.log("ProcessedMethods Interface: " + element);
369                hasWebMethods = false;
370                for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
371                    method.accept(this, null);
372                }
373                for (TypeMirror superType : element.getInterfaces())
374                    processMethods((TypeElement) ((DeclaredType) superType).asElement());
375                break;
376            }
377            case CLASS: {
378                builder.log("ProcessedMethods Class: " + element);
379                hasWebMethods = hasWebMethods(element);
380                if (element.getQualifiedName().toString().equals(Object.class.getName()))
381                    return;
382                if (element.getAnnotation(WebService.class) != null) {
383                    // Super classes must have @WebService annotations to pick up their methods
384                    for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
385                        method.accept(this, null);
386                    }
387                }
388                TypeMirror superclass = element.getSuperclass();
389                if (!superclass.getKind().equals(TypeKind.NONE)) {
390                    processMethods((TypeElement) ((DeclaredType) superclass).asElement());
391                }
392                break;
393            }
394            default:
395                break;
396        }
397    }
398
399    private TypeElement getEndpointInterfaceElement(String endpointInterfaceName, TypeElement element) {
400        TypeElement intTypeElement = null;
401        for (TypeMirror interfaceType : element.getInterfaces()) {
402            if (endpointInterfaceName.equals(interfaceType.toString())) {
403                intTypeElement = (TypeElement) ((DeclaredType) interfaceType).asElement();
404                seiContext = context.getSeiContext(intTypeElement.getQualifiedName());
405                assert (seiContext != null);
406                seiContext.setImplementsSei(true);
407                break;
408            }
409        }
410        if (intTypeElement == null) {
411            intTypeElement = builder.getProcessingEnvironment().getElementUtils().getTypeElement(endpointInterfaceName);
412        }
413        if (intTypeElement == null)
414            builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTERFACE_CLASS_NOT_FOUND(endpointInterfaceName));
415        return intTypeElement;
416    }
417
418    private void inspectEndpointInterface(String endpointInterfaceName, TypeElement d) {
419        TypeElement intTypeElement = getEndpointInterfaceElement(endpointInterfaceName, d);
420        if (intTypeElement != null)
421            intTypeElement.accept(this, null);
422    }
423
424    @Override
425    public Void visitExecutable(ExecutableElement method, Object o) {
426        // Methods must be public
427        if (!method.getModifiers().contains(Modifier.PUBLIC))
428            return null;
429        if (processedMethod(method))
430            return null;
431        WebMethod webMethod = method.getAnnotation(WebMethod.class);
432        if (webMethod != null && webMethod.exclude())
433            return null;
434        SOAPBinding soapBinding = method.getAnnotation(SOAPBinding.class);
435        if (soapBinding == null && !method.getEnclosingElement().equals(typeElement)) {
436            if (method.getEnclosingElement().getKind().equals(ElementKind.CLASS)) {
437                soapBinding = method.getEnclosingElement().getAnnotation(SOAPBinding.class);
438                if (soapBinding != null)
439                    builder.log("using " + method.getEnclosingElement() + "'s SOAPBinding.");
440                else {
441                    soapBinding = new MySoapBinding();
442                }
443            }
444        }
445        boolean newBinding = false;
446        if (soapBinding != null) {
447            newBinding = pushSoapBinding(soapBinding, method, typeElement);
448        }
449        try {
450            if (shouldProcessMethod(method, webMethod)) {
451                processMethod(method, webMethod);
452            }
453        } finally {
454            if (newBinding) {
455                popSoapBinding();
456            }
457        }
458        return null;
459    }
460
461    protected boolean processedMethod(ExecutableElement method) {
462        String id = method.toString();
463        if (processedMethods.contains(id))
464            return true;
465        processedMethods.add(id);
466        return false;
467    }
468
469    protected boolean shouldProcessMethod(ExecutableElement method, WebMethod webMethod) {
470        builder.log("should process method: " + method.getSimpleName() + " hasWebMethods: " + hasWebMethods + " ");
471        /*
472        Fix for https://jax-ws.dev.java.net/issues/show_bug.cgi?id=577
473        if (hasWebMethods && webMethod == null) {
474            builder.log("webMethod == null");
475            return false;
476        }
477        */
478        Collection<Modifier> modifiers = method.getModifiers();
479        boolean staticFinal = modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.FINAL);
480        if (staticFinal) {
481            if (webMethod != null) {
482                builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_METHOD_IS_STATIC_OR_FINAL(method.getEnclosingElement(),
483                        method), method);
484            }
485            return false;
486        }
487        boolean result = (endpointReferencesInterface ||
488                method.getEnclosingElement().equals(typeElement) ||
489                (method.getEnclosingElement().getAnnotation(WebService.class) != null));
490        builder.log("endpointReferencesInterface: " + endpointReferencesInterface);
491        builder.log("declaring class has WebService: " + (method.getEnclosingElement().getAnnotation(WebService.class) != null));
492        builder.log("returning: " + result);
493        return result;
494    }
495
496    abstract protected void processMethod(ExecutableElement method, WebMethod webMethod);
497
498    protected boolean isLegalImplementation(WebService webService, TypeElement classElement) {
499        boolean isStateful = isStateful(classElement);
500
501        Collection<Modifier> modifiers = classElement.getModifiers();
502        if (!modifiers.contains(Modifier.PUBLIC)) {
503            builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_CLASS_NOT_PUBLIC(classElement.getQualifiedName()), classElement);
504            return false;
505        }
506        if (modifiers.contains(Modifier.FINAL) && !isStateful) {
507            builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_CLASS_IS_FINAL(classElement.getQualifiedName()), classElement);
508            return false;
509        }
510        if (modifiers.contains(Modifier.ABSTRACT) && !isStateful) {
511            builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_CLASS_IS_ABSTRACT(classElement.getQualifiedName()), classElement);
512            return false;
513        }
514        boolean hasDefaultConstructor = false;
515        for (ExecutableElement constructor : ElementFilter.constructorsIn(classElement.getEnclosedElements())) {
516            if (constructor.getModifiers().contains(Modifier.PUBLIC) &&
517                    constructor.getParameters().isEmpty()) {
518                hasDefaultConstructor = true;
519                break;
520            }
521        }
522        if (!hasDefaultConstructor && !isStateful) {
523            if (classElement.getEnclosingElement() != null && !modifiers.contains(Modifier.STATIC)) {
524                builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_CLASS_IS_INNERCLASS_NOT_STATIC(
525                        classElement.getQualifiedName()), classElement);
526                return false;
527            }
528
529            builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_NO_DEFAULT_CONSTRUCTOR(
530                    classElement.getQualifiedName()), classElement);
531            return false;
532        }
533        if (webService.endpointInterface().isEmpty()) {
534            if (!methodsAreLegal(classElement))
535                return false;
536        } else {
537            TypeElement interfaceElement = getEndpointInterfaceElement(webService.endpointInterface(), classElement);
538            if (!classImplementsSei(classElement, interfaceElement))
539                return false;
540        }
541
542        return true;
543    }
544
545    private boolean isStateful(TypeElement classElement) {
546        try {
547            // We don't want dependency on rt-ha module as its not integrated in JDK
548            return classElement.getAnnotation((Class<? extends Annotation>) Class.forName("com.sun.xml.internal.ws.developer.Stateful")) != null;
549        } catch (ClassNotFoundException e) {
550            //ignore
551        }
552        return false;
553    }
554
555    protected boolean classImplementsSei(TypeElement classElement, TypeElement interfaceElement) {
556        for (TypeMirror interfaceType : classElement.getInterfaces()) {
557            if (((DeclaredType) interfaceType).asElement().equals(interfaceElement))
558                return true;
559        }
560        List<ExecutableElement> classMethods = getClassMethods(classElement);
561        boolean implementsMethod;
562        for (ExecutableElement interfaceMethod : ElementFilter.methodsIn(interfaceElement.getEnclosedElements())) {
563            implementsMethod = false;
564            for (ExecutableElement classMethod : classMethods) {
565                if (sameMethod(interfaceMethod, classMethod)) {
566                    implementsMethod = true;
567                    classMethods.remove(classMethod);
568                    break;
569                }
570            }
571            if (!implementsMethod) {
572                builder.processError(WebserviceapMessages.WEBSERVICEAP_METHOD_NOT_IMPLEMENTED(interfaceElement.getSimpleName(), classElement.getSimpleName(), interfaceMethod), interfaceMethod);
573                return false;
574            }
575        }
576        return true;
577    }
578
579    private static List<ExecutableElement> getClassMethods(TypeElement classElement) {
580        if (classElement.getQualifiedName().toString().equals(Object.class.getName())) // we don't need Object's methods
581            return null;
582        TypeElement superclassElement = (TypeElement) ((DeclaredType) classElement.getSuperclass()).asElement();
583        List<ExecutableElement> superclassesMethods = getClassMethods(superclassElement);
584        List<ExecutableElement> classMethods = ElementFilter.methodsIn(classElement.getEnclosedElements());
585        if (superclassesMethods == null)
586            return classMethods;
587        else
588            superclassesMethods.addAll(classMethods);
589        return superclassesMethods;
590    }
591
592    protected boolean sameMethod(ExecutableElement method1, ExecutableElement method2) {
593        if (!method1.getSimpleName().equals(method2.getSimpleName()))
594            return false;
595        Types typeUtils = builder.getProcessingEnvironment().getTypeUtils();
596        if(!typeUtils.isSameType(method1.getReturnType(), method2.getReturnType())
597                && !typeUtils.isSubtype(method2.getReturnType(), method1.getReturnType()))
598            return false;
599        List<? extends VariableElement> parameters1 = method1.getParameters();
600        List<? extends VariableElement> parameters2 = method2.getParameters();
601        if (parameters1.size() != parameters2.size())
602            return false;
603        for (int i = 0; i < parameters1.size(); i++) {
604            if (!typeUtils.isSameType(parameters1.get(i).asType(), parameters2.get(i).asType()))
605                return false;
606        }
607        return true;
608    }
609
610    protected boolean isLegalSei(TypeElement interfaceElement) {
611        return methodsAreLegal(interfaceElement);
612    }
613
614    protected boolean methodsAreLegal(TypeElement element) {
615        switch (element.getKind()) {
616            case INTERFACE: {
617                hasWebMethods = false;
618                for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
619                    if (!isLegalMethod(method, element))
620                        return false;
621                }
622                for (TypeMirror superInterface : element.getInterfaces()) {
623                    if (!methodsAreLegal((TypeElement) ((DeclaredType) superInterface).asElement()))
624                        return false;
625                }
626                return true;
627            }
628            case CLASS: {
629                hasWebMethods = hasWebMethods(element);
630                for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
631                    if (!method.getModifiers().contains(Modifier.PUBLIC))
632                        continue; // let's validate only public methods
633                    if (!isLegalMethod(method, element))
634                        return false;
635                }
636                DeclaredType superClass = (DeclaredType) element.getSuperclass();
637
638                TypeElement tE = (TypeElement) superClass.asElement();
639                return tE.getQualifiedName().toString().equals(Object.class.getName())
640                        || methodsAreLegal(tE);
641            }
642            default: {
643                throw new IllegalArgumentException("Class or interface was expecting. But element: " + element);
644            }
645        }
646    }
647
648    protected boolean isLegalMethod(ExecutableElement method, TypeElement typeElement) {
649        WebMethod webMethod = method.getAnnotation(WebMethod.class);
650        //SEI cannot have methods with @WebMethod(exclude=true)
651        if (typeElement.getKind().equals(ElementKind.INTERFACE) && webMethod != null && webMethod.exclude())
652            builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SEI_ANNOTATION_ELEMENT_EXCLUDE("exclude=true", typeElement.getQualifiedName(), method.toString()), method);
653        // With https://jax-ws.dev.java.net/issues/show_bug.cgi?id=577, hasWebMethods has no effect
654        if (hasWebMethods && webMethod == null) // backwards compatibility (for legacyWebMethod computation)
655            return true;
656
657        if ((webMethod != null) && webMethod.exclude()) {
658            return true;
659        }
660        /*
661        This check is not needed as Impl class is already checked that it is not abstract.
662        if (typeElement instanceof TypeElement && method.getModifiers().contains(Modifier.ABSTRACT)) {  // use Kind.equals instead of instanceOf
663            builder.processError(method.getPosition(), WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_METHOD_IS_ABSTRACT(typeElement.getQualifiedName(), method.getSimpleName()));
664            return false;
665        }
666        */
667        TypeMirror returnType = method.getReturnType();
668        if (!isLegalType(returnType)) {
669            builder.processError(WebserviceapMessages.WEBSERVICEAP_METHOD_RETURN_TYPE_CANNOT_IMPLEMENT_REMOTE(typeElement.getQualifiedName(),
670                    method.getSimpleName(),
671                    returnType), method);
672        }
673        boolean isOneWay = method.getAnnotation(Oneway.class) != null;
674        if (isOneWay && !isValidOneWayMethod(method, typeElement))
675            return false;
676
677        SOAPBinding soapBinding = method.getAnnotation(SOAPBinding.class);
678        if (soapBinding != null) {
679            if (soapBinding.style().equals(SOAPBinding.Style.RPC)) {
680                builder.processError(WebserviceapMessages.WEBSERVICEAP_RPC_SOAPBINDING_NOT_ALLOWED_ON_METHOD(typeElement.getQualifiedName(), method.toString()), method);
681            }
682        }
683
684        int paramIndex = 0;
685        for (VariableElement parameter : method.getParameters()) {
686            if (!isLegalParameter(parameter, method, typeElement, paramIndex++))
687                return false;
688        }
689
690        if (!isDocLitWrapped() && soapStyle.equals(SOAPStyle.DOCUMENT)) {
691            VariableElement outParam = getOutParameter(method);
692            int inParams = getModeParameterCount(method, WebParam.Mode.IN);
693            int outParams = getModeParameterCount(method, WebParam.Mode.OUT);
694            if (inParams != 1) {
695                builder.processError(WebserviceapMessages.WEBSERVICEAP_DOC_BARE_AND_NO_ONE_IN(typeElement.getQualifiedName(), method.toString()), method);
696            }
697            if (returnType.accept(NO_TYPE_VISITOR, null)) {
698                if (outParam == null && !isOneWay) {
699                    builder.processError(WebserviceapMessages.WEBSERVICEAP_DOC_BARE_NO_OUT(typeElement.getQualifiedName(), method.toString()), method);
700                }
701                if (outParams != 1) {
702                    if (!isOneWay && outParams != 0)
703                        builder.processError(WebserviceapMessages.WEBSERVICEAP_DOC_BARE_NO_RETURN_AND_NO_OUT(typeElement.getQualifiedName(), method.toString()), method);
704                }
705            } else {
706                if (outParams > 0) {
707                    builder.processError(WebserviceapMessages.WEBSERVICEAP_DOC_BARE_RETURN_AND_OUT(typeElement.getQualifiedName(), method.toString()), outParam);
708                }
709            }
710        }
711        return true;
712    }
713
714    protected boolean isLegalParameter(VariableElement param,
715                                       ExecutableElement method,
716                                       TypeElement typeElement,
717                                       int paramIndex) {
718        if (!isLegalType(param.asType())) {
719            builder.processError(WebserviceapMessages.WEBSERVICEAP_METHOD_PARAMETER_TYPES_CANNOT_IMPLEMENT_REMOTE(typeElement.getQualifiedName(),
720                    method.getSimpleName(),
721                    param.getSimpleName(),
722                    param.asType().toString()), param);
723            return false;
724        }
725        TypeMirror holderType;
726        holderType = builder.getHolderValueType(param.asType());
727        WebParam webParam = param.getAnnotation(WebParam.class);
728        WebParam.Mode mode = null;
729        if (webParam != null)
730            mode = webParam.mode();
731
732        if (holderType != null) {
733            if (mode != null && mode == WebParam.Mode.IN)
734                builder.processError(WebserviceapMessages.WEBSERVICEAP_HOLDER_PARAMETERS_MUST_NOT_BE_IN_ONLY(typeElement.getQualifiedName(), method.toString(), paramIndex), param);
735        } else if (mode != null && mode != WebParam.Mode.IN) {
736            builder.processError(WebserviceapMessages.WEBSERVICEAP_NON_IN_PARAMETERS_MUST_BE_HOLDER(typeElement.getQualifiedName(), method.toString(), paramIndex), param);
737        }
738
739        return true;
740    }
741
742    protected boolean isDocLitWrapped() {
743        return soapStyle.equals(SOAPStyle.DOCUMENT) && wrapped;
744    }
745
746    private static final class NoTypeVisitor extends SimpleTypeVisitor6<Boolean, Void> {
747
748        @Override
749        public Boolean visitNoType(NoType t, Void o) {
750            return true;
751        }
752
753        @Override
754        protected Boolean defaultAction(TypeMirror e, Void aVoid) {
755            return false;
756        }
757    }
758
759    protected boolean isValidOneWayMethod(ExecutableElement method, TypeElement typeElement) {
760        boolean valid = true;
761        if (!(method.getReturnType().accept(NO_TYPE_VISITOR, null))) {
762            // this is an error, cannot be OneWay and have a return type
763            builder.processError(WebserviceapMessages.WEBSERVICEAP_ONEWAY_OPERATION_CANNOT_HAVE_RETURN_TYPE(typeElement.getQualifiedName(), method.toString()), method);
764            valid = false;
765        }
766        VariableElement outParam = getOutParameter(method);
767        if (outParam != null) {
768            builder.processError(WebserviceapMessages.WEBSERVICEAP_ONEWAY_AND_OUT(typeElement.getQualifiedName(), method.toString()), outParam);
769            valid = false;
770        }
771        if (!isDocLitWrapped() && soapStyle.equals(SOAPStyle.DOCUMENT)) {
772            int inCnt = getModeParameterCount(method, WebParam.Mode.IN);
773            if (inCnt != 1) {
774                builder.processError(WebserviceapMessages.WEBSERVICEAP_ONEWAY_AND_NOT_ONE_IN(typeElement.getQualifiedName(), method.toString()), method);
775                valid = false;
776            }
777        }
778        for (TypeMirror thrownType : method.getThrownTypes()) {
779            TypeElement thrownElement = (TypeElement) ((DeclaredType) thrownType).asElement();
780            if (builder.isServiceException(thrownType)) {
781                builder.processError(WebserviceapMessages.WEBSERVICEAP_ONEWAY_OPERATION_CANNOT_DECLARE_EXCEPTIONS(
782                        typeElement.getQualifiedName(), method.toString(), thrownElement.getQualifiedName()), method);
783                valid = false;
784            }
785        }
786        return valid;
787    }
788
789    protected int getModeParameterCount(ExecutableElement method, WebParam.Mode mode) {
790        WebParam webParam;
791        int cnt = 0;
792        for (VariableElement param : method.getParameters()) {
793            webParam = param.getAnnotation(WebParam.class);
794            if (webParam != null) {
795                if (webParam.header())
796                    continue;
797                if (isEquivalentModes(mode, webParam.mode()))
798                    cnt++;
799            } else {
800                if (isEquivalentModes(mode, WebParam.Mode.IN)) {
801                    cnt++;
802                }
803            }
804        }
805        return cnt;
806    }
807
808    protected boolean isEquivalentModes(WebParam.Mode mode1, WebParam.Mode mode2) {
809        if (mode1.equals(mode2))
810            return true;
811        assert mode1 == WebParam.Mode.IN || mode1 == WebParam.Mode.OUT;
812        return (mode1 == WebParam.Mode.IN && mode2 != WebParam.Mode.OUT) || (mode1 == WebParam.Mode.OUT && mode2 != WebParam.Mode.IN);
813    }
814
815    protected boolean isHolder(VariableElement param) {
816        return builder.getHolderValueType(param.asType()) != null;
817    }
818
819    protected boolean isLegalType(TypeMirror type) {
820        if (!(type != null && type.getKind().equals(TypeKind.DECLARED)))
821            return true;
822        TypeElement tE = (TypeElement) ((DeclaredType) type).asElement();
823        if (tE == null) {
824            // can be null, if this type's declaration is unknown. This may be the result of a processing error, such as a missing class file.
825            builder.processError(WebserviceapMessages.WEBSERVICEAP_COULD_NOT_FIND_TYPEDECL(type.toString(), context.getRound()));
826        }
827        return !builder.isRemote(tE);
828    }
829
830    protected VariableElement getOutParameter(ExecutableElement method) {
831        WebParam webParam;
832        for (VariableElement param : method.getParameters()) {
833            webParam = param.getAnnotation(WebParam.class);
834            if (webParam != null && webParam.mode() != WebParam.Mode.IN) {
835                return param;
836            }
837        }
838        return null;
839    }
840
841    protected static class MySoapBinding implements SOAPBinding {
842
843        @Override
844        public Style style() {
845            return SOAPBinding.Style.DOCUMENT;
846        }
847
848        @Override
849        public Use use() {
850            return SOAPBinding.Use.LITERAL;
851        }
852
853        @Override
854        public ParameterStyle parameterStyle() {
855            return SOAPBinding.ParameterStyle.WRAPPED;
856        }
857
858        @Override
859        public Class<? extends java.lang.annotation.Annotation> annotationType() {
860            return SOAPBinding.class;
861        }
862    }
863}
864