1 /*
   2  * Copyright (c) 2015, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package compiler.compilercontrol.share.method;
  25 
  26 import jdk.test.lib.Triple;
  27 
  28 import java.lang.reflect.Executable;
  29 import java.util.function.Function;
  30 import java.util.regex.Pattern;
  31 
  32 /**
  33  * Method descriptor for Compiler Control commands.
  34  * It represents method pattern used for matching in Compiler Control
  35  * and CompileCommand option
  36  */
  37 public class MethodDescriptor {
  38     public final ClassType aClass;         // Represents class and package
  39     public final MethodType aMethod;       // Represents method
  40     public final SignatureType aSignature; // Represents signature
  41 
  42     /**
  43      * Constructor
  44      *
  45      * @param method executable to build method descriptor from
  46      */
  47     public MethodDescriptor(Executable method) {
  48         aClass = new ClassType(method);
  49         aMethod = new MethodType(method);
  50         aSignature = new SignatureType(method);
  51     }
  52 
  53     /**
  54      * Sets signature separators for all elements
  55      */
  56     public void setSeparators(
  57             Triple<Separator, Separator, Separator> separators) {
  58         aClass.setSeparator(separators.getFirst());
  59         aMethod.setSeparator(separators.getSecond());
  60         aSignature.setSeparator(separators.getThird());
  61     }
  62 
  63     /**
  64      * Sets custom strings for each element
  65      */
  66     public void setStrings(Triple<String, String, String> strings) {
  67         aClass.setElement(strings.getFirst());
  68         aMethod.setElement(strings.getSecond());
  69         aSignature.setElement(strings.getThird());
  70     }
  71 
  72     /**
  73      * Sets patterns for all elements
  74      */
  75     public void setPatterns(
  76             Triple<PatternType, PatternType, PatternType> patterns) {
  77         aClass.setPattern(patterns.getFirst());
  78         aMethod.setPattern(patterns.getSecond());
  79         aSignature.setPattern(patterns.getThird());
  80     }
  81 
  82     /**
  83      * Separates elements in the MethodDescriptor
  84      */
  85     public static enum Separator {
  86         SLASH("/"),
  87         DOT("."),
  88         COMMA(","),
  89         DOUBLECOLON("::"),
  90         SPACE(" "),
  91         NONE("");
  92 
  93         public final String symbol;
  94 
  95         Separator(String symbol) {
  96             this.symbol = symbol;
  97         }
  98 
  99         /**
 100          * Validates method descriptor separators
 101          *
 102          * @param md method descriptor to validate
 103          * @return true if descriptor's separators are valid
 104          */
 105         public static boolean isValid(MethodDescriptor md) {
 106             Separator cls = md.getClassSeparator();
 107             Separator method = md.getMethodSeparator();
 108             Separator sign = md.getSignatureSeparator();
 109             if (sign == SPACE || sign == NONE || sign == COMMA) {
 110                 // if it looks like java/lang/String.indexOf
 111                 if ((cls == SLASH || cls == NONE)
 112                         // allow space and comma instead of dot
 113                         && (method == DOT || method == SPACE
 114                         || method == COMMA)) {
 115                     return true;
 116                 }
 117                 // if it looks like java.lang.String::indexOf
 118                 if ((cls == DOT || cls == NONE) && method == DOUBLECOLON) {
 119                     return true;
 120                 }
 121             }
 122             return false;
 123         }
 124     }
 125 
 126     /**
 127      * Type of the pattern
 128      */
 129     public static enum PatternType {
 130         PREFIX,
 131         ANY,
 132         SUFFIX,
 133         SUBSTRING,
 134         EXACT
 135     }
 136 
 137     public Separator getClassSeparator() {
 138         return aClass.getSeparator();
 139     }
 140 
 141     public Separator getMethodSeparator() {
 142         return aMethod.getSeparator();
 143     }
 144 
 145     public Separator getSignatureSeparator() {
 146         return aSignature.getSeparator();
 147     }
 148 
 149     /**
 150      * Gets regular expression to match methods
 151      *
 152      * @return string representation of the regular expression
 153      */
 154     public String getRegexp() {
 155         // regexp should have a . as a method separator
 156         // and / as a package/class separator
 157         return aClass.getRegexp().replaceAll("\\.", "/")
 158                 .replaceAll("/\\*", ".*")
 159                 + Pattern.quote(Separator.DOT.symbol)
 160                 + aMethod.getRegexp() + aSignature.getRegexp();
 161     }
 162 
 163     /**
 164      * Gets method descriptor string representation.
 165      * This string is used as a pattern in CompilerControl and CompileCommand
 166      */
 167     public String getString() {
 168         return aClass.getElement() + getMethodSeparator().symbol
 169                 + aMethod.getElement() + getSignatureSeparator().symbol
 170                 + aSignature.getElement();
 171     }
 172 
 173     /**
 174      * Convert method descriptor to be regexp-compatible
 175      *
 176      * @return string representation of the method signature
 177      */
 178     public String getCanonicalString() {
 179         return aClass.getElement().replaceAll("\\.", "/") + Separator.DOT.symbol
 180                 + aMethod.getElement() + aSignature.getElement();
 181     }
 182 
 183     /**
 184      * Shows if this descriptor is a valid pattern for CompilerControl
 185      *
 186      * @return true, if descriptor is valid, false otherwise
 187      */
 188     public boolean isValid() {
 189         return aClass.isValid() && aMethod.isValid() && aSignature.isValid()
 190                 && Separator.isValid(this);
 191     }
 192 
 193     /**
 194      * Sets custom string from element mutate function
 195      * to the appropriate element of method descriptor
 196      */
 197     public void applyMutates(Triple<Function<String, String>,
 198                              Function<String, String>,
 199                              Function<String, String>> mutators) {
 200         String elementString = aClass.getElement();
 201         aClass.setElement(mutators.getFirst().apply(elementString));
 202         elementString = aMethod.getElement();
 203         aMethod.setElement(mutators.getSecond().apply(elementString));
 204         elementString = aSignature.getElement();
 205         aSignature.setElement(mutators.getThird().apply(elementString));
 206     }
 207 }