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 'Q':
 139                 case 'L':
 140                     int sep = desc.indexOf(';', p);
 141                     if (sep == -1 || sep >= end)
 142                         throw ex(desc, p);
 143                     type = desc.substring(p, ++sep);
 144                     p = sep;
 145                     break;
 146 
 147                 default:
 148                     throw ex(desc, p);
 149             }
 150 
 151             StringBuilder sb = new StringBuilder();
 152             for ( ; dims > 0; dims-- )
 153                 sb.append("[");
 154             sb.append(type);
 155             if (inReturnType) {
 156                 returnType = sb.toString();
 157             } else {
 158                 parameters.add(sb.toString());
 159             }
 160         }
 161 
 162         if (returnType == null) {
 163             throw ex(desc, end);
 164         }
 165 
 166         return new MethodSig(parameters, returnType);
 167     }
 168 }