1/*
2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.tools.jdeprscan.scan;
27
28import java.util.ArrayList;
29import java.util.Collections;
30import java.util.List;
31
32/**
33 * Represents a method's signature, that is, its parameter types
34 * and its return type.
35 */
36public class MethodSig {
37    final List<String> parameters;
38    final String returnType;
39
40    /**
41     * Parses the method descriptor and returns a MethodSig instance.
42     *
43     * @param desc the descriptor to parse
44     * @return the new MethodSig instance
45     */
46    public static MethodSig fromDesc(String desc) {
47        return parse(desc, 0, desc.length());
48    }
49
50    /**
51     * Returns this method's return type.
52     *
53     * @return the return type
54     */
55    public String getReturnType() {
56        return returnType;
57    }
58
59    /**
60     * Returns a list of parameters of this method.
61     *
62     * @return the parameter list
63     */
64    public List<String> getParameters() {
65        return parameters;
66    }
67
68    /**
69     * Returns a string describing this method.
70     *
71     * @return the string description
72     */
73    @Override
74    public String toString() {
75        StringBuilder sb = new StringBuilder();
76        sb.append("parameters");
77        if (parameters.isEmpty()) {
78            sb.append(" none");
79        } else {
80            int i = 0;
81            for (String p : parameters) {
82                sb.append(String.format(" %d=%s", i++, p));
83            }
84        }
85        sb.append(String.format(" return %s", returnType));
86        return sb.toString();
87    }
88
89    private MethodSig(List<String> parameters, String returnType) {
90        this.parameters = Collections.unmodifiableList(parameters);
91        this.returnType = returnType;
92    }
93
94    private static IllegalArgumentException ex(String desc, int pos) {
95        return new IllegalArgumentException(String.format(
96            "illegal descriptor \"%s\" at position %d", desc, pos));
97    }
98
99    private static MethodSig parse(String desc, int start, int end)
100            throws IllegalArgumentException {
101        int p = start;
102        int dims = 0;
103        boolean inReturnType = false;
104        String returnType = null;
105        List<String> parameters = new ArrayList<>();
106
107        while (p < end) {
108            String type;
109            char ch;
110            switch (ch = desc.charAt(p)) {
111                case '(':
112                    p++;
113                    continue;
114
115                case ')':
116                    p++;
117                    inReturnType = true;
118                    continue;
119
120                case '[':
121                    p++;
122                    dims++;
123                    continue;
124
125                case 'B': // byte
126                case 'C': // char
127                case 'D': // double
128                case 'F': // float
129                case 'I': // int
130                case 'J': // long
131                case 'S': // short
132                case 'Z': // boolean
133                case 'V': // void
134                    type = Character.toString(ch);
135                    p++;
136                    break;
137
138                case 'L':
139                    int sep = desc.indexOf(';', p);
140                    if (sep == -1 || sep >= end)
141                        throw ex(desc, p);
142                    type = desc.substring(p, ++sep);
143                    p = sep;
144                    break;
145
146                default:
147                    throw ex(desc, p);
148            }
149
150            StringBuilder sb = new StringBuilder();
151            for ( ; dims > 0; dims-- )
152                sb.append("[");
153            sb.append(type);
154            if (inReturnType) {
155                returnType = sb.toString();
156            } else {
157                parameters.add(sb.toString());
158            }
159        }
160
161        if (returnType == null) {
162            throw ex(desc, end);
163        }
164
165        return new MethodSig(parameters, returnType);
166    }
167}
168