从类源代码文件中检索方法源代码

时间:2010-12-08 12:22:00

标签: java parsing

我这里有一个包含类源代码的String。现在我有另一个String,其中包含此类中方法的全名。方法名称是例如。

public void (java.lang.String test)

现在我想从带有类源代码的字符串中读取此方法的源代码。我怎样才能做到这一点?使用String#indexOf(methodName),我可以找到方法源代码的开头,但我如何找到结束?

==== EDIT ====

我使用了计数大括号的方法:

 internal void retrieveSourceCode()
        {
            int startPosition = parentClass.getSourceCode().IndexOf(this.getName());
            if (startPosition != -1)
            {
                String subCode = parentClass.getSourceCode().Substring(startPosition, parentClass.getSourceCode().Length - startPosition);

                for (int i = 0; i < subCode.Length; i++)
                {
                    String c = subCode.Substring(0, i);
                    int open = c.Split('{').Count() - 1;
                    int close = c.Split('}').Count() - 1;

                    if (open == close && open != 0)
                    {
                        sourceCode = c;
                        break;
                    }
                }

            }
            Console.WriteLine("SourceCode for " + this.getName() + "\n" + sourceCode);
        }

这或多或少都有效,但是,如果定义一个没有body的方法,它就会失败。任何提示如何解决?

3 个答案:

答案 0 :(得分:1)

请查看: - Parser for C#

它建议使用NRefactory来解析和标记源代码,您应该能够使用它来导航类源并选择方法。

答案 1 :(得分:1)

计数括号并在计数减少到0时停止确实是要走的路。当然,您需要考虑以文字显示的大括号,因此不应计算,例如评论和字符串中的括号。

总的来说,这是一种吃力不讨好的努力,在复杂性方面可以说,如果你想让它真正可靠地工作,那就建立一个命令行解析器。如果你知道你可以逃脱它,你可以削减一些角落,只计算所有的牙套,虽然我不推荐它。

<强>更新

这是一些用于进行大括号计数的示例代码。正如我所说,这是一个吃力不讨好的工作,你需要做大量的细节(实质上,你正在写一个迷你词法分子)。它位于C#中,因为它是最接近Java的,我可以放心地编写代码。

下面的代码不完整,可能不是100%正确(例如:C#中的逐字字符串不允许@和开头引号之间的空格,但是我知道这是为了一个事实还是只是忘了它?)

// sourceCode is a string containing all the source file's text
var sourceCode = "...";

// startIndex is the index of the char AFTER the opening brace
// for the method we are interested in
var methodStartIndex = 42;

var openBraces = 1;
var insideLiteralString = false;
var insideVerbatimString = false;
var insideBlockComment = false;
var lastChar = ' '; // White space is ignored by the C# parser,
                    // so a space is a good "neutral" character

for (var i = methodStartIndex; openBraces > 0; ++i) {
    var ch = sourceCode[i];

    switch (ch) {
        case '{':
            if (!insideBlockComment && !insideLiteralString && !insideVerbatimString) {
                ++openBraces;
            }
            break;
        case '}':
            if (!insideBlockComment && !insideLiteralString && !insideVerbatimString) {
                --openBraces;
            }
            break;
        case '"':
            if (insideBlockComment) {
                continue;
            }
            if (insideLiteralString) {
                // "Step out" of the string if this is the closing quote
                insideLiteralString = lastChar != '\';
            }
            else if (insideVerbatimString) {
                // If this quote is part of a two-quote pair, do NOT step out
                // (it means the string contains a literal quote)

                // This can throw, but only for source files with syntax errors
                // I 'm ignoring this possibility here...
                var nextCh = sourceCode[i + 1]; 

                if (nextCh == '"') {
                    ++i; // skip that next quote
                }
                else {
                    insideVerbatimString = false;
                }
            }
            else {
                if (lastChar == '@') {
                    insideVerbatimString = true;
                }
                else {
                    insideLiteralString = true;
                }
            }
            break;
        case '/':
            if (insideLiteralString || insideVerbatimString) {
                continue;
            }

            // TODO: parse this
            // It can start a line comment, if followed by /
            // It can start a block comment, if followed by *
            // It can end a block comment, if preceded by *

            // Line comments are intended to be handled by just incrementing i
            // until you see a CR and/or LF, hence no insideLineComment flag.
            break;
    }

    lastChar = ch;
}

// From the values of methodStartIndex and i we can now do sourceCode.Substring and get the method source

答案 2 :(得分:1)

您可能必须知道代码文件中列出的方法序列。这样,您可以查找关闭范围}的方法,该方法可能位于下一个方法的开头。

所以您的代码可能如下所示:

nStartOfMethod = String.indexOf(methodName)
nStartOfNextMethod = String.indexOf(NextMethodName)

.LastIndexOf(yourMethodTerminator /*probably a*/,...)

字符串之间寻找nStartOfMethod} nStartOfNextMethod

在这种情况下,如果你知道方法序列,你可能最终会在其间跳过一个方法,以找到一个结束括号。