001: /*
002: * Copyright 2004-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.springframework.binding.method;
017:
018: import java.util.LinkedList;
019: import java.util.List;
020:
021: import org.springframework.binding.convert.ConversionContext;
022: import org.springframework.binding.convert.ConversionException;
023: import org.springframework.binding.convert.ConversionService;
024: import org.springframework.binding.convert.support.ConversionServiceAwareConverter;
025: import org.springframework.binding.expression.Expression;
026:
027: /**
028: * Converter that takes an encoded string representation and produces a
029: * corresponding <code>MethodSignature</code> object.
030: * <p>
031: * This converter supports the following encoded forms:
032: * <ul>
033: * <li> "methodName" - the name of the method to invoke, where the method is
034: * expected to have no arguments. </li>
035: * <li> "methodName(param1Type param1Name, paramNType paramNName)" - the name of
036: * the method to invoke, where the method is expected to have parameters
037: * delimited by a comma. In this example, the method has two parameters. The
038: * type is either the fully-qualified class of the argument OR a known type
039: * alias OR left out althogether. The name is the logical name of the argument,
040: * which is used during data binding to retrieve the argument value
041: * (typically an expression). </li>
042: * </ul>
043: *
044: * @see MethodSignature
045: *
046: * @author Keith Donald
047: * @author Erwin Vervaet
048: */
049: public class TextToMethodSignature extends
050: ConversionServiceAwareConverter {
051:
052: /**
053: * Create a new converter that converts strings to MethodSignature objects.
054: */
055: public TextToMethodSignature() {
056: }
057:
058: /**
059: * Create a new converter that converts strings to MethodSignature objects.
060: * @param conversionService the conversion service to use
061: */
062: public TextToMethodSignature(ConversionService conversionService) {
063: super (conversionService);
064: }
065:
066: public Class[] getSourceClasses() {
067: return new Class[] { String.class };
068: }
069:
070: public Class[] getTargetClasses() {
071: return new Class[] { MethodSignature.class };
072: }
073:
074: protected Object doConvert(Object source, Class targetClass,
075: ConversionContext context) throws Exception {
076: String encodedMethodSignature = (String) source;
077: encodedMethodSignature = encodedMethodSignature.trim();
078: int openParan = encodedMethodSignature.indexOf('(');
079: if (openParan == -1) {
080: // form "foo"
081: return new MethodSignature(encodedMethodSignature);
082: } else {
083: // form "foo(...)"
084: String methodName = encodedMethodSignature.substring(0,
085: openParan);
086: int closeParan = encodedMethodSignature.lastIndexOf(')');
087: if (closeParan != (encodedMethodSignature.length() - 1)) {
088: throw new ConversionException(
089: encodedMethodSignature,
090: MethodSignature.class,
091: "Syntax error: No close parenthesis specified for method parameter list",
092: null);
093: }
094: String delimitedParams = encodedMethodSignature.substring(
095: openParan + 1, closeParan);
096: String[] paramArray = splitParameters(
097: encodedMethodSignature, delimitedParams);
098: Parameters params = new Parameters(paramArray.length);
099: for (int i = 0; i < paramArray.length; i++) {
100: // param could be of the form "type name", "name", "type ${name}" or "${name}"
101: String param = paramArray[i].trim();
102: int space = param.indexOf(' ');
103: int expr = param.indexOf('{');
104: if (space == -1 || (expr != -1 && space > expr)) {
105: // "name" or "${name}"
106: params.add(new Parameter(null,
107: parseExpression(param)));
108: } else {
109: // "type name" or "type ${name}"
110: Class type = (Class) fromStringTo(Class.class)
111: .execute(param.substring(0, space).trim());
112: Expression name = parseExpression(param.substring(
113: space + 1).trim());
114: params.add(new Parameter(type, name));
115: }
116: }
117: return new MethodSignature(methodName, params);
118: }
119: }
120:
121: /**
122: * Split given parameter string into individual parameter definitions.
123: */
124: private String[] splitParameters(String encodedMethodSignature,
125: String parameters) {
126: List res = new LinkedList();
127:
128: int paramStart = 0;
129: int blockNestingCount = 0;
130: for (int i = 0; i < parameters.length(); i++) {
131: switch (parameters.charAt(i)) {
132: case '{':
133: blockNestingCount++;
134: break;
135: case '}':
136: blockNestingCount--;
137: break;
138: case ',':
139: if (blockNestingCount == 0) {
140: // only take comma delimiter into account when not inside
141: // a block
142: res.add(parameters.substring(paramStart, i));
143: paramStart = i + 1;
144: }
145: break;
146: }
147: }
148: if (blockNestingCount != 0) {
149: throw new ConversionException(encodedMethodSignature,
150: MethodSignature.class,
151: "Syntax error: Curly braces do not match", null);
152: }
153: if (paramStart < parameters.length()) {
154: res.add(parameters.substring(paramStart));
155: }
156:
157: return (String[]) res.toArray(new String[res.size()]);
158: }
159: }
|