| /* |
| * Copyright 2003-2004 The Apache Software Foundation. |
| // (c) Copyright IBM Corp. 2004, 2005 All Rights Reserved |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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 org.apache.axis.tools.common; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * A C or C++ method signature with the ability to parse it. |
| * TODO: properly support variable length argument lists using "..." |
| * TODO: passing or returning function pointers (hopefully not needed) |
| * TODO: Cope with ~<space>Classname() |
| */ |
| public class Signature { |
| private String originalText; |
| private String attributes; |
| private String className = null; |
| private String methodName = null; |
| private Parameter returnType = null; |
| private Parameter[] params = null; |
| private String trailingAttributes; |
| private String scope = "public"; |
| private boolean failed = false; |
| private boolean traceable = true; |
| |
| private final static Set knownAttrs = |
| new HashSet( |
| Arrays.asList( |
| new Object[] { |
| "public", |
| "private", |
| "extern", |
| "\"C\"", |
| "virtual", |
| "static", |
| "inline" })); |
| |
| private final static Set specialOperators = |
| new HashSet( |
| Arrays.asList( |
| new Object[] { "(", ")", "*", ",", "&", "]", "[", "=", "~" })); |
| |
| /** |
| * Takes an unparsed signature string and parses it. |
| * |
| * TODO: Should optionally pass in the className here in case it's an |
| * inline method implementation inside the class{}. Just so the className |
| * comes out in the trace. |
| */ |
| Signature(String s) { |
| originalText = s; |
| |
| try { |
| List tokens = tokenise(s); |
| |
| ArrayList alAttrs = new ArrayList(); |
| ArrayList alName = new ArrayList(); |
| ArrayList alParms = new ArrayList(); |
| ArrayList alTrailAttrs = new ArrayList(); |
| ArrayList alInits = new ArrayList(); |
| if (!splitUp(tokens, |
| alAttrs, |
| alName, |
| alParms, |
| alTrailAttrs, |
| alInits)) { |
| failed = true; |
| return; |
| } |
| |
| parseAttributes(alAttrs); |
| parseNameAndRetType(alName); |
| parseParameters(alParms); |
| parseTrailingAttributes(alTrailAttrs); |
| |
| // Ignore any tokens after the ) since these are (hopefully) |
| // constructor initialisers |
| |
| traceable = !Configuration.methodExcluded(className, methodName); |
| } catch (NullPointerException npe) { |
| failed = true; |
| traceable = false; |
| } |
| } |
| |
| /** |
| * Parse the signature into tokens. This removes whitespace and comments |
| * and separates out "*", ",", "(", ")", "&", "[" and "]". |
| */ |
| private static List tokenise(String s) { |
| ArrayList tokens = new ArrayList(); |
| String tok = null; |
| boolean space = true; |
| for (int i = 0; i < s.length(); i++) { |
| char c = s.charAt(i); |
| if (Character.isWhitespace(c)) { |
| space = true; |
| continue; |
| } |
| if (space) { |
| if (tok != null) |
| tokens.add(tok); |
| tok = "" + c; |
| } else |
| tok += c; |
| space = false; |
| |
| if (tok.endsWith("/*")) { |
| String sub = s.substring(i); |
| int endcomm = sub.indexOf("*/"); |
| if (endcomm == -1) |
| break; |
| i += endcomm + 1; |
| if (tok.equals("/*")) |
| tok = ""; |
| else |
| tok = tok.substring(0, tok.length() - 2); |
| continue; |
| } |
| |
| if (tok.endsWith("//")) { |
| String sub = s.substring(i); |
| int endcomm = sub.indexOf("\n"); |
| if (endcomm == -1) |
| break; |
| i += endcomm; |
| if (tok.equals("//")) |
| tok = ""; |
| else |
| tok = tok.substring(0, tok.length() - 1); |
| continue; |
| } |
| |
| if (tok.endsWith("::")) |
| space = true; |
| |
| String sc = "" + c; |
| if (specialOperators.contains(sc)) { |
| if (!tok.equals(sc)) { |
| tokens.add(tok.substring(0, tok.length() - 1)); |
| tok = sc; |
| } |
| space = true; |
| } |
| } |
| tokens.add(tok); |
| return tokens; |
| } |
| |
| /** |
| * Split up a tokenised method signature into a list of attributes, a list of |
| * name and return type tokens, a list of parameter tokens and a list of |
| * initialiser tokens. |
| */ |
| private static boolean splitUp( |
| List tokens, |
| List attrs, |
| List nameAndRet, |
| List parms, |
| List trailAttrs, |
| List inits) { |
| |
| // nameStart points to the start of the return type if there is one |
| // else the start of the method name |
| int nameStart; |
| for (nameStart = 0; nameStart < tokens.size(); nameStart++) { |
| String tok = (String) (tokens.get(nameStart)); |
| if (!knownAttrs.contains(tok) && !Configuration.isAttribute(tok)) |
| break; |
| } |
| if (nameStart == tokens.size()) |
| return false; |
| |
| // initStart points to the initialisers, or thrown exceptions after |
| // the parameter list. throw is a keyword so we can safely search for it. |
| int initStart = tokens.size(); |
| for (int i = nameStart; i < tokens.size(); i++) { |
| String tok = (String) tokens.get(i); |
| if ((tok.startsWith(":") && !tok.startsWith("::")) |
| || "throw".equals(tok)) |
| initStart = i; |
| } |
| |
| int parmEnd; |
| for (parmEnd = initStart - 1; parmEnd > nameStart; parmEnd--) |
| if (")".equals(tokens.get(parmEnd))) |
| break; |
| if (parmEnd == nameStart) |
| return false; |
| |
| int parmStart = parmEnd; |
| for (parmStart = parmEnd; parmStart > nameStart; parmStart--) |
| if ("(".equals(tokens.get(parmStart))) |
| break; |
| |
| for (int i = 0; i < tokens.size(); i++) { |
| Object tok = tokens.get(i); |
| if (i < nameStart || Configuration.isAttribute((String) tok)) |
| attrs.add(tok); |
| else if (i < parmStart) |
| nameAndRet.add(tok); |
| else if (i <= parmEnd) |
| parms.add(tok); |
| else if (i < initStart) |
| trailAttrs.add(tok); |
| else |
| inits.add(tok); |
| } |
| return true; |
| } |
| |
| private void parseAttributes(List list) { |
| attributes = new String(); |
| Iterator it = list.iterator(); |
| while (it.hasNext()) { |
| if (attributes.length() > 0) |
| attributes += " "; |
| String next = (String) it.next(); |
| if ("public".equals(next) |
| || "protected".equals(next) |
| || "private".equals(next)) |
| scope = next; |
| attributes += next; |
| } |
| } |
| |
| private void parseNameAndRetType(List list) { |
| int size = list.size(); |
| int idx; |
| // "operator" is a key word so if it's present we know we're |
| // dealing with operator overloading. The operator that's been |
| // overloaded might have been split up into multiple tokens. |
| for (idx = 0; idx < size; idx++) |
| if ("operator".equals(list.get(idx))) |
| break; |
| |
| if (idx < size) { |
| methodName = ""; |
| for (int i = idx; i < size; i++) |
| methodName += (String) list.get(i); |
| } else { // No operator overloading |
| methodName = "" + list.get(size - 1); |
| idx = size - 1; |
| } |
| |
| // If it's a destructor, the "~" will be split out into a separate |
| // token, so add it onto the methodName here. |
| if (idx > 0 && "~".equals(list.get(idx - 1))) { |
| methodName = "~" + methodName; |
| idx--; |
| } |
| |
| // The class name comes before the method name |
| while (idx > 0 && ((String) list.get(idx - 1)).endsWith("::")) { |
| if (null == className) |
| className = (String) list.get(idx - 1); |
| else |
| className = (String) list.get(idx - 1) + className; |
| idx--; |
| } |
| |
| // Whatever's left before the classname/methodname must be the |
| // return type |
| ArrayList retParm = new ArrayList(); |
| for (int i = 0; i < idx; i++) |
| retParm.add(list.get(i)); |
| |
| returnType = new Parameter(retParm, true); |
| } |
| |
| /** |
| * Constructs the parameter list |
| */ |
| private void parseParameters(List list) { |
| ArrayList alParams = new ArrayList(); |
| Iterator it = list.iterator(); |
| String token = (String) it.next(); // step over the ( |
| while (it.hasNext() && !")".equals(token)) { |
| token = (String) it.next(); |
| |
| int template = 0; // Depth of template scope |
| boolean foundEquals = false; |
| // Ignore default value for an optional parameter |
| ArrayList parm = new ArrayList(); |
| while (!token.equals(")") |
| && (!token.equals(",") || template > 0)) { |
| if (token.equals("=")) |
| foundEquals = true; |
| if (!foundEquals) |
| parm.add(token); |
| if (contains(token, "<")) |
| template++; |
| if (contains(token, ">")) |
| template--; |
| token = (String) it.next(); |
| } |
| |
| // No parameters so break out |
| if (token.equals(")") && 0 == parm.size()) |
| break; |
| |
| Parameter p = new Parameter(parm); |
| if (p.failed()) { |
| failed = true; |
| return; |
| } |
| |
| // Copes with void func(void) |
| if (!p.isVoid()) |
| alParams.add(p); |
| } |
| |
| int size = alParams.size(); |
| if (size > 0) { |
| params = new Parameter[size]; |
| System.arraycopy(alParams.toArray(), 0, params, 0, size); |
| } |
| } |
| |
| private void parseTrailingAttributes(List list) { |
| trailingAttributes = new String(); |
| Iterator it = list.iterator(); |
| while (it.hasNext()) { |
| if (trailingAttributes.length() > 0) |
| trailingAttributes += " "; |
| trailingAttributes += (String) it.next(); |
| } |
| } |
| |
| public String getOriginal() { |
| return originalText; |
| } |
| |
| public int originalLength() { |
| return originalText.length(); |
| } |
| |
| public boolean failed() { |
| return failed; |
| } |
| |
| public String getAttributes() { |
| return attributes; |
| } |
| |
| public String getClassName() { |
| return className; |
| } |
| |
| public String getTrimClassName() { |
| return trimClassName(className); |
| } |
| |
| public String getMethodName() { |
| return methodName; |
| } |
| |
| public Parameter getReturnType() { |
| return returnType; |
| } |
| |
| public Parameter[] getParameters() { |
| return params; |
| } |
| |
| public boolean isConstructor() { |
| return className != null |
| && methodName != null |
| && trimClassName(className).equals(methodName); |
| } |
| |
| public boolean isDestructor() { |
| return className != null |
| && methodName != null |
| && methodName.startsWith("~") |
| && methodName.endsWith(trimClassName(className)); |
| } |
| |
| private static String trimClassName(String name) { |
| if (name.endsWith("::")) |
| return name.substring(0, name.length() - 2); |
| return name; |
| } |
| |
| void setClassName(String className) { |
| if (null == className) |
| return; |
| if (!className.endsWith("::")) |
| className += "::"; |
| this.className = className; |
| } |
| |
| public String getScope() { |
| return scope; |
| } |
| |
| /** |
| * Sets the scope, but only if the scope is not set by an explicit |
| * attribute in the signature. |
| */ |
| public void setScope(String scope) { |
| if (-1 == attributes.indexOf(this.scope)) |
| this.scope = scope; |
| } |
| |
| /** |
| * Should this method be traced? |
| */ |
| public boolean traceable() { |
| return traceable; |
| } |
| |
| private static boolean contains(String src, String tgt) { |
| if (src == null || tgt == null) |
| return false; |
| if (-1 == src.indexOf(tgt)) |
| return false; |
| return true; |
| } |
| |
| public boolean equals(Object obj) { |
| if (null == obj || !(obj instanceof Signature)) |
| return false; |
| Signature that = (Signature) obj; |
| if (!Utils.safeEquals(className, that.className)) |
| return false; |
| if (!Utils.safeEquals(methodName, that.methodName)) |
| return false; |
| if (!Utils.safeEquals(returnType, that.returnType)) |
| return false; |
| if (null == params && null == that.params) |
| return true; |
| if (null != params && null == that.params) |
| return false; |
| if (null == params && null != that.params) |
| return false; |
| if (params.length != that.params.length) |
| return false; |
| for (int i = 0; i < params.length; i++) |
| if (!Utils.safeEquals(params[i], that.params[i])) |
| return false; |
| return true; |
| } |
| |
| public String toStringWithoutAttrs() { |
| String s = new String(); |
| if (returnType != null) |
| s += returnType + " "; |
| if (className != null) |
| s += className; |
| s += methodName + "("; |
| for (int i = 0; params != null && i < params.length; i++) { |
| if (i > 0) |
| s += ", "; |
| s += params[i].toString(); |
| } |
| s += ")"; |
| return s; |
| } |
| |
| public String toString() { |
| String s = attributes; |
| if (attributes.length() > 0) |
| s += " "; |
| s += toStringWithoutAttrs(); |
| if (trailingAttributes.length() > 0) |
| s += " " + trailingAttributes; |
| return s; |
| } |
| } |