| /* |
| * 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.io.BufferedReader; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.StringTokenizer; |
| |
| public class InputCppSourceCode { |
| |
| private ArrayList parts = new ArrayList(); |
| private String name; |
| |
| public InputCppSourceCode(BufferedReader br, String name) throws Exception { |
| this.name = name; |
| |
| String s = null; |
| StringBuffer buff = new StringBuffer(); |
| for (int i = 1;; i++) { |
| try { |
| s = br.readLine(); |
| } catch (Exception e) { |
| System.err.println( |
| "Ignoring exception thrown parsing file " |
| + name |
| + " line number " |
| + i); |
| e.printStackTrace(); |
| break; |
| } |
| if (s == null) |
| break; |
| buff.append(s + "\n"); |
| } |
| String str = buff.toString(); |
| |
| // TODO: When checking for rest.startsWith("struct") should |
| // check the next letter after struct is not alphanumeric otherwise |
| // we'll get false matches on a function called structify() for |
| // instance. Also applies to enum, union, public, typedef, etc |
| |
| String rest, text = ""; |
| int scopedepth = 0; |
| String scope = "public"; |
| String currentClass = null; |
| for (int idx = 0; idx < str.length(); /* No idx++ */ |
| ) { |
| rest = str.substring(idx); |
| if (Character.isWhitespace(rest.charAt(0))) { |
| int ridx = 0; |
| while (ridx < rest.length() |
| && Character.isWhitespace(rest.charAt(ridx))) |
| ridx++; |
| text = rest.substring(0, ridx); |
| FilePart fp = new FilePart(text, FilePart.WHITESPACE); |
| parts.add(fp); |
| idx += ridx; |
| |
| } else if (rest.startsWith("/*")) { |
| int ridx = rest.indexOf("*/"); // Don't use Utils here |
| text = str.substring(idx, idx + ridx + 2); |
| FilePart fp = new FilePart(text, FilePart.COMMENT); |
| parts.add(fp); |
| idx += text.length(); |
| |
| } else if (rest.startsWith("//")) { |
| text = str.substring(idx, idx + rest.indexOf("\n")); |
| FilePart fp = new FilePart(text, FilePart.COMMENT); |
| parts.add(fp); |
| idx += text.length(); |
| |
| } else if (rest.startsWith("#")) { |
| int ridx = rest.indexOf("\n"); |
| char c = rest.charAt(ridx - 1); |
| while (-1 != ridx && '\\' == c) { |
| String rest2 = rest.substring(ridx + 1); |
| ridx += rest2.indexOf("\n") + 1; |
| c = rest.charAt(ridx - 1); |
| } |
| text = str.substring(idx, idx + ridx); |
| FilePart fp = new FilePart(text, FilePart.DIRECTIVE); |
| parts.add(fp); |
| idx += text.length(); |
| |
| } else if (rest.startsWith("}")) { |
| if (scopedepth <= 0) |
| Utils.rude( |
| "Braces do not match", |
| name, |
| lineNo(str, idx), |
| rest.substring(0, rest.indexOf("\n"))); |
| else |
| scopedepth--; |
| // TODO: better checking that this brace really ends the class |
| if (0 == scopedepth) |
| currentClass = null; |
| scope = "public"; |
| parts.add(new FilePart("}", FilePart.ENDSCOPE)); |
| idx++; |
| |
| } else if (rest.startsWith(";")) { |
| parts.add(new FilePart(";", FilePart.FIELD)); |
| idx++; |
| |
| } else if ( |
| !Character.isLetter(rest.charAt(0)) |
| && '~' != rest.charAt(0) |
| && '_' != rest.charAt(0)) { |
| Utils.rude( |
| "Lines must start with a letter ", |
| name, |
| lineNo(str, idx), |
| rest.substring(0, rest.indexOf("\n"))); |
| |
| } else if (MacroPart.isAMacro(rest)) { |
| MacroPart mp = MacroPart.create(rest); |
| parts.add(mp); |
| idx += mp.length(); |
| |
| } else if (beginsScope(rest)) { |
| scopedepth++; |
| text = rest.substring(0, Utils.indexOf(rest, "{") + 1); |
| FilePart fp = new FilePart(text, FilePart.BEGINSCOPE); |
| parts.add(fp); |
| idx += text.length(); |
| if (Utils.startsWith(text, "class")) { |
| // TODO: cope with comments here |
| // TODO: split out classes into a ClassPart |
| StringTokenizer st = |
| new StringTokenizer(text, Utils.whitespace + ":"); |
| st.nextToken(); // step over "class" |
| while (st.hasMoreTokens()) { |
| String word = st.nextToken(); |
| if (Configuration.isAttribute(word)) |
| continue; |
| currentClass = word; |
| break; |
| } |
| } |
| |
| } else if (isEnumOrUnion(rest)) { |
| int ridx = Utils.findMatching(rest, '{', '}') + 1; |
| String rest2 = rest.substring(ridx); |
| ridx = idx + ridx + Utils.indexOf(rest2, ';') + 1; |
| text = str.substring(idx, ridx); |
| FilePart fp = new FilePart(text, FilePart.ENUM); |
| parts.add(fp); |
| idx += text.length(); |
| |
| } else if ( |
| scopedepth > 0 |
| && (rest.startsWith("public") |
| || rest.startsWith("protected") |
| || rest.startsWith("private"))) { |
| int colon = rest.indexOf(":"); |
| if (-1 == colon) |
| Utils.rude( |
| "No colon found after public or private ", |
| name, |
| lineNo(str, idx), |
| rest.substring(0, rest.indexOf("\n"))); |
| scope = str.substring(idx, idx + colon); |
| text = str.substring(idx, idx + colon + 1); |
| FilePart fp = new FilePart(text, FilePart.CLASSATTRIBUTE); |
| parts.add(fp); |
| idx += text.length(); |
| |
| } else if (Utils.startsWith(rest, "typedef")) { |
| int semicolon = Utils.indexOf(rest, ';'); |
| int brace = Utils.indexOf(rest, '{'); |
| |
| if (-1 == semicolon) |
| Utils.rude( |
| "No semicolon found after typedef", |
| name, |
| lineNo(str, idx), |
| rest.substring(0, rest.indexOf("\n"))); |
| |
| if (-1 == brace || semicolon < brace) { |
| // Simple typedef |
| text = str.substring(idx, idx + semicolon + 1); |
| } else { |
| // Typedef of a struct, etc |
| int endbrace = Utils.findMatching(rest, '{', '}'); |
| String rest2 = rest.substring(endbrace); |
| semicolon = Utils.indexOf(rest2, ';'); |
| text = str.substring(idx, idx + endbrace + semicolon + 1); |
| } |
| FilePart fp = new FilePart(text, FilePart.TYPEDEF); |
| parts.add(fp); |
| idx += text.length(); |
| |
| } else { |
| if (isMethod(rest)) { |
| |
| int brace = Utils.indexOf(rest, '{'); |
| Signature signature = |
| new Signature(str.substring(idx, idx + brace)); |
| if (signature.failed()) |
| Utils.rude( |
| "Signature parsing failed", |
| name, |
| lineNo(str, idx), |
| signature.getOriginal()); |
| if (null != currentClass |
| && null == signature.getClassName()) |
| signature.setClassName(currentClass); |
| signature.setScope(scope); |
| |
| String body = rest.substring(brace); |
| int endBrace = Utils.findMatching(body, '{', '}'); |
| body = body.substring(0, endBrace + 1); |
| int endIdx = |
| idx + signature.originalLength() + body.length(); |
| text = str.substring(idx, endIdx); |
| MethodPart mp = |
| new MethodPart(text, signature, body); |
| parts.add(mp); |
| idx += text.length(); |
| |
| } else if (isField(rest)) { |
| int semicolon = Utils.indexOf(rest, ';'); |
| text = str.substring(idx, idx + semicolon + 1); |
| FilePart fp = new FilePart(text, FilePart.FIELD); |
| parts.add(fp); |
| idx += text.length(); |
| |
| } else if (isPrototype(rest)) { |
| int semicolon = Utils.indexOf(rest, ';'); |
| text = str.substring(idx, idx + semicolon + 1); |
| PrototypePart pp = new PrototypePart(text, currentClass); |
| pp.setScope(scope); |
| parts.add(pp); |
| idx += text.length(); |
| |
| } else { |
| //TODO other file parts here - not sure if there are any others? |
| Utils.rude( |
| "Unrecognised file part", |
| name, |
| lineNo(str, idx), |
| rest.substring(0, rest.indexOf("\n"))); |
| } // end if |
| } // end if |
| } // end for |
| } |
| |
| public Iterator getPartIterator() { |
| return parts.iterator(); |
| } |
| |
| private int lineNo(String s, int idx) { |
| int n = 0; |
| for (int i = 0; i < idx && i < s.length(); i++) |
| if ('\n' == s.charAt(i)) |
| n++; |
| return n; |
| } |
| |
| /** |
| * Find out whether we are defining a class, struct or extern "C" which |
| * may contain function implementations. These will have braces which |
| * begin a new scope. Ignore function prototypes that return a struct. |
| * struct mystr { int f1; }; |
| * struct mystr func(); |
| * struct mystr func() { struct mystr a; return a; } |
| */ |
| private static boolean beginsScope(String s) throws ParsingException { |
| if (isMethod(s)) |
| return false; |
| |
| int brace = Utils.indexOf(s, '{'); |
| int semicolon = Utils.indexOf(s, ';'); |
| |
| // Return false for class prototypes, but true for class definitions. |
| if (Utils.startsWith(s, "class")) { |
| if (-1 == brace) |
| return false; |
| if (-1 == semicolon) |
| return true; |
| return brace < semicolon; |
| } |
| |
| if (Utils.startsWith(s, "struct")) { |
| if (-1 == brace || -1 == semicolon) |
| return false; |
| return brace < semicolon; |
| } |
| return startsWithExternScope(s); |
| } |
| |
| /** |
| * There are 4 types of extern ... |
| * extern int field; |
| * extern int func(); |
| * extern "C" int func() { return 2; } |
| * extern "C" { int func() { return 2; } } |
| * This method should return true only for the last of these three |
| * examples since only the last one creates a new scope using braces. |
| */ |
| private static boolean startsWithExternScope(String s) |
| throws ParsingException { |
| if (!s.startsWith("extern")) |
| return false; |
| |
| int brace = Utils.indexOf(s, '{'); |
| int semicolon = Utils.indexOf(s, ';'); |
| int bracket = Utils.indexOf(s, '('); |
| |
| if (-1 == brace) |
| return false; |
| return (-1 == semicolon || brace < semicolon) |
| && (-1 == bracket || brace < bracket); |
| } |
| |
| /** |
| * Find out whether we are defining an enum or union which will contain |
| * braces. Ignore function prototypes that return an enum or union. |
| * enum colour { red, blue }; |
| * enum colour func(); |
| * enum colour func() { return colour.red; } |
| */ |
| private static boolean isEnumOrUnion(String s) throws ParsingException { |
| if ((!Utils.startsWith(s, "enum") && !Utils.startsWith(s, "union")) |
| || isMethod(s)) |
| return false; |
| |
| int brace = Utils.indexOf(s, '{'); |
| int semicolon = Utils.indexOf(s, ';'); |
| return -1 != brace && (-1 == semicolon || brace < semicolon); |
| } |
| |
| /** |
| * Rules to recognise fields and methods... |
| * |
| * Fields must contain a semicolon |
| * Methods may or may not contain a semicolon |
| * Prototypes must contain a semicolon |
| * Fields may or may not contain a brace (array initialisers do) |
| * Methods must contain a brace |
| * Prototypes must not contain a brace |
| * Fields may or may not contain a bracket (casts do) |
| * Methods must contain a bracket |
| * Prototypes must contain a bracket |
| * |
| * It's a method if it contains a bracket and then a brace |
| * before the first semicolon (if there is a semicolon). |
| * It's a prototype if it's not a method and it contains brackets before a semicolon. |
| * It's a field if it's not a method or a prototype and it contains a semicolon. |
| * If it's not a field, a method or a prototype and we haven't recognised it |
| * previously then it's an error. |
| */ |
| private static boolean isMethod(String s) throws ParsingException { |
| int semicolon = Utils.indexOf(s, ';'); |
| int brace = Utils.indexOf(s, '{'); |
| int bracket = Utils.indexOf(s, '('); |
| |
| return ( |
| -1 != bracket |
| && -1 != brace |
| && bracket < brace |
| && (-1 == semicolon || brace < semicolon)); |
| } |
| |
| private static boolean isPrototype(String s) throws ParsingException { |
| int semicolon = Utils.indexOf(s, ';'); |
| int bracket = Utils.indexOf(s, '('); |
| return !isMethod(s) |
| && -1 != semicolon |
| && -1 != bracket |
| && bracket < semicolon; |
| } |
| |
| private static boolean isField(String s) throws ParsingException { |
| int semicolon = Utils.indexOf(s, ';'); |
| return !isMethod(s) && !isPrototype(s) && -1 != semicolon; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public String toString() { |
| StringBuffer text = new StringBuffer(); |
| for (int i = 0; i < parts.size(); i++) { |
| text.append(((FilePart) (parts.get(i))).toString()); |
| } |
| return text.toString(); |
| } |
| |
| } |