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 
 26 package com.sun.tools.jdeprscan.scan;
 27 
 28 import java.util.ArrayList;
 29 import java.util.Collections;
 30 import java.util.List;
 31 
 32 /**
 33  * Represents a method's signature, that is, its parameter types
 34  * and its return type.
 35  */
 36 public 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 }