001: /*
002: Copyright (c) 2007, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.ws.wsdl;
030:
031: import java.util.ArrayList;
032: import java.util.List;
033: import java.util.Set;
034:
035: import org.jibx.binding.generator.CustomBase;
036: import org.jibx.binding.model.DocumentFormatter;
037: import org.jibx.binding.model.IClass;
038: import org.jibx.binding.model.IClassItem;
039: import org.jibx.binding.model.IClassLocator;
040: import org.jibx.runtime.IUnmarshallingContext;
041: import org.jibx.runtime.JiBXException;
042: import org.jibx.runtime.impl.UnmarshallingContext;
043:
044: /**
045: * Operation customization information. This supports direct operation
046: * customizations (such as the corresponding request and/or response element
047: * name) and also acts as a container for parameter and/or return
048: * customizations.
049: */
050: public class OperationCustom extends NestingBase {
051: // values specific to class level
052: private String m_methodName;
053: private String m_operationName;
054: private String m_requestMessageName;
055: private String m_requestWrapperName;
056: private String m_responseMessageName;
057: private String m_responseWrapperName;
058: private String m_soapAction;
059: private List m_documentation;
060: private String[] m_requireds;
061: private String[] m_optionals;
062:
063: // list of contained parameter customizations
064: private final ArrayList m_parameters;
065:
066: // contained result customization (null if none)
067: private ValueCustom m_return;
068:
069: // list of contained throws customizations
070: private final ArrayList m_throws;
071:
072: /**
073: * Constructor.
074: *
075: * @param parent
076: * @param name method name
077: */
078: OperationCustom(NestingBase parent, String name) {
079: super (parent);
080: m_methodName = name;
081: m_parameters = new ArrayList();
082: m_throws = new ArrayList();
083: }
084:
085: /**
086: * Get the namespace for WSDL definitions of this service.
087: *
088: * @return WSDL namespace
089: */
090: public String getWsdlNamespace() {
091: return ((NestingBase) getParent()).getWsdlNamespace();
092: }
093:
094: /**
095: * Get method name.
096: *
097: * @return name
098: */
099: public String getMethodName() {
100: return m_methodName;
101: }
102:
103: /**
104: * Get the operation name.
105: *
106: * @return operation name
107: */
108: public String getOperationName() {
109: return m_operationName;
110: }
111:
112: /**
113: * Get request message name.
114: *
115: * @return name
116: */
117: public String getRequestMessageName() {
118: return m_requestMessageName;
119: }
120:
121: /**
122: * Get request wrapper element name.
123: *
124: * @return name
125: */
126: public String getRequestWrapperName() {
127: return m_requestWrapperName;
128: }
129:
130: /**
131: * Get response message name.
132: *
133: * @return name
134: */
135: public String getResponseMessageName() {
136: return m_responseMessageName;
137: }
138:
139: /**
140: * Get response wrapper name.
141: *
142: * @return name
143: */
144: public String getResponseWrapperName() {
145: return m_responseWrapperName;
146: }
147:
148: /**
149: * Get return value.
150: *
151: * @return return
152: */
153: public ValueCustom getReturn() {
154: return m_return;
155: }
156:
157: /**
158: * Get SOAPAction.
159: *
160: * @return soapAction
161: */
162: public String getSoapAction() {
163: return m_soapAction;
164: }
165:
166: /**
167: * Get operation documentation.
168: *
169: * @return list of documentation nodes (<code>null</code> if none)
170: */
171: public List getDocumentation() {
172: return m_documentation;
173: }
174:
175: /**
176: * Get list of children.
177: *
178: * @return list
179: */
180: public ArrayList getParameters() {
181: return m_parameters;
182: }
183:
184: /**
185: * Get list of throws customizations.
186: *
187: * @return list
188: */
189: public ArrayList getThrows() {
190: return m_throws;
191: }
192:
193: /**
194: * Add child.
195: *
196: * @param child
197: */
198: protected void addChild(CustomBase child) {
199: if (child.getParent() == this ) {
200: m_parameters.add(child);
201: } else {
202: throw new IllegalStateException(
203: "Internal error: child not linked");
204: }
205: }
206:
207: /**
208: * Unmarshalling factory. This gets the containing element and the name so
209: * that the standard constructor can be used.
210: *
211: * @param ictx
212: * @return created instance
213: * @throws JiBXException
214: */
215: private static OperationCustom factory(IUnmarshallingContext ictx)
216: throws JiBXException {
217: UnmarshallingContext uctx = (UnmarshallingContext) ictx;
218: return new OperationCustom(
219: (NestingBase) getContainingClass(ictx), uctx
220: .attributeText(null, "method-name"));
221: }
222:
223: /**
224: * Check if type is a collection type (specifically collection, not array).
225: *
226: * @param type
227: * @return item type, <code>null</code> if not a collection type
228: */
229: private boolean isCollection(String type, IClassLocator icl) {
230: IClass info = icl.getClassInfo(type);
231: return info.isImplements("Ljava/util/Collection;");
232: }
233:
234: /**
235: * Parse parameter type.
236: *
237: * @param parse
238: * @return parameter type
239: */
240: private String parameterType(SignatureParser parse) {
241: String itype = null;
242: while (parse.next() != SignatureParser.TYPE_PARAMETERS_END_EVENT) {
243: if (itype == null
244: && parse.getEvent() == SignatureParser.TYPE_EVENT) {
245: itype = parse.getType();
246: }
247: }
248: return itype;
249: }
250:
251: /**
252: * Build value representation. The value may be either a simple value or a
253: * collection value.
254: *
255: * @param name
256: * @param itype item type (<code>null</code> if not a collection)
257: * @return value
258: */
259: private ValueCustom buildValue(String name, String itype) {
260: if (itype == null) {
261: ValueCustom parm = new ValueCustom(this , name);
262: return parm;
263: } else {
264: CollectionValueCustom parm = new CollectionValueCustom(
265: this , name);
266: return parm;
267: }
268: }
269:
270: /**
271: * Check if a particular value is required or optional.
272: *
273: * @param name
274: * @param reqset
275: * @param optset
276: * @return <code>TRUE</code> if required, <code>FALSE</code> if optional,
277: * <code>null</code> if unknown
278: */
279: private static Boolean checkRequired(String name, Set reqset,
280: Set optset) {
281: if (reqset != null && reqset.contains(name)) {
282: return Boolean.TRUE;
283: } else if (optset != null && optset.contains(name)) {
284: return Boolean.FALSE;
285: } else {
286: return null;
287: }
288: }
289:
290: /**
291: * Apply customizations to method to fill out parameter and return
292: * information.
293: *
294: * @param method
295: * @param icl
296: * @param fmt
297: */
298: public void apply(IClassItem method, IClassLocator icl,
299: DocumentFormatter fmt) {
300:
301: // fill in missing details
302: if (m_operationName == null) {
303: String name = convertName(m_methodName, CAMEL_CASE_NAMES);
304: m_operationName = registerName(name, this );
305: } else if (!m_operationName.equals(registerName(
306: m_operationName, this ))) {
307: throw new IllegalStateException(
308: "Operation name conflict for '" + m_operationName
309: + '\'');
310: }
311: if (m_requestMessageName == null) {
312: m_requestMessageName = m_operationName + "Message";
313: }
314: if (m_requestWrapperName == null) {
315: m_requestWrapperName = m_operationName;
316: }
317: if (m_responseMessageName == null) {
318: m_responseMessageName = m_operationName + "ResponseMessage";
319: }
320: if (m_responseWrapperName == null) {
321: m_responseWrapperName = m_operationName + "Response";
322: }
323: if (m_soapAction == null && isSoapAction()) {
324: m_soapAction = "urn:" + m_operationName;
325: }
326: if (m_documentation == null) {
327: m_documentation = fmt.docToNodes(method.getJavaDoc());
328: }
329:
330: // find parameter types, and item types for collections
331: int count = method.getArgumentCount();
332: String[] ptypes = new String[count];
333: String[] pitypes = new String[count];
334: String rtype = null;
335: String ritype = null;
336: String sig = method.getGenericsSignature();
337: if (sig == null) {
338:
339: // no signature, just use basic type information
340: for (int i = 0; i < count; i++) {
341: String type = method.getArgumentType(i);
342: ptypes[i] = type;
343: if (isCollection(type, icl)) {
344: pitypes[i] = "java.lang.Object";
345: }
346: }
347: rtype = method.getTypeName();
348: if (isCollection(rtype, icl)) {
349: ritype = "java.lang.Object";
350: }
351:
352: } else {
353:
354: // parse the signature to check collection item types
355: SignatureParser parse = new SignatureParser(sig);
356: int index = 0;
357: boolean inparms = false;
358: while (parse.next() != SignatureParser.END_EVENT) {
359: switch (parse.getEvent()) {
360:
361: case SignatureParser.METHOD_PARAMETERS_START_EVENT:
362: inparms = true;
363: index = 0;
364: break;
365:
366: case SignatureParser.METHOD_PARAMETERS_END_EVENT:
367: inparms = false;
368: break;
369:
370: case SignatureParser.TYPE_EVENT:
371: String type = parse.getType();
372: String itype = null;
373: if (parse.isParameterized()) {
374: String ptype = parameterType(parse);
375: IClass info = icl.getClassInfo(type);
376: if (info.isImplements("Ljava/util/Collection;")) {
377: itype = ptype;
378: }
379: }
380: if (inparms) {
381: ptypes[index] = type;
382: pitypes[index++] = itype;
383: } else {
384: rtype = type;
385: ritype = itype;
386: }
387: break;
388:
389: }
390: }
391: }
392:
393: // fill in the parameters and return customizations
394: Set reqset = nameSet(m_requireds);
395: Set optset = nameSet(m_optionals);
396: if (m_parameters.size() == 0) {
397: for (int i = 0; i < count; i++) {
398: String name = method.getParameterName(i);
399: if (name == null) {
400: name = "arg" + (i + 1);
401: }
402: m_parameters.add(buildValue(name, pitypes[i]));
403: }
404: } else {
405: if (m_parameters.size() != count) {
406: // TODO: merge generated with supplied parameter information?
407: throw new IllegalStateException("Wrong parameter count");
408: }
409: }
410: for (int i = 0; i < count; i++) {
411: ValueCustom param = (ValueCustom) m_parameters.get(i);
412: String name = param.getValueName();
413: param.setElementName(convertName(name, CAMEL_CASE_NAMES));
414: String ptype = ptypes[i];
415: String pitype = pitypes[i];
416: Boolean req = checkRequired(name, reqset, optset);
417: List docs = fmt.docToNodes(method.getParameterJavaDoc(i));
418: if (param instanceof CollectionValueCustom) {
419: ((CollectionValueCustom) param).complete(ptype, pitype,
420: docs, req);
421: } else {
422: param.complete(ptype, docs, req);
423: }
424: }
425: Boolean req = checkRequired("return", reqset, optset);
426: String text = method.getReturnJavaDoc();
427: boolean isname = false;
428: if (text != null
429: && Character.isJavaIdentifierStart(text.charAt(0))) {
430: isname = true;
431: for (int i = 1; i < text.length(); i++) {
432: if (!Character.isJavaIdentifierPart(text.charAt(i))) {
433: isname = false;
434: break;
435: }
436: }
437: }
438: String name = "return";
439: if (isname) {
440: name = text;
441: text = null;
442: }
443: List docs = fmt.docToNodes(text);
444: if (m_return == null) {
445: m_return = buildValue(name, ritype);
446: }
447: if (m_return instanceof CollectionValueCustom) {
448: ((CollectionValueCustom) m_return).complete(rtype, ritype,
449: docs, req);
450: } else {
451: m_return.complete(rtype, docs, req);
452: }
453:
454: // add throws information
455: count = method.getExceptions().length;
456: if (m_throws.size() == 0) {
457: for (int i = 0; i < count; i++) {
458: name = method.getExceptions()[i];
459: ThrowsCustom thrw = new ThrowsCustom(this, name);
460: thrw.complete(fmt.docToNodes(method
461: .getExceptionJavaDoc(i)));
462: m_throws.add(thrw);
463: }
464: }
465: }
466: }
|