1 /*
  2  * Copyright (c) 2017, 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 jdk.test.lib.combo;
 25 
 26 import java.util.regex.Matcher;
 27 import java.util.regex.Pattern;
 28 
 29 /**
 30  * A combo parameter represents an 'hole' in a template that can be replaced with a given string.
 31  * The schema of such holes is defined in {@link ComboParameter#pattern}; the main routine for
 32  * replacing holes in a template scheme is {@link ComboParameter#expandTemplate(String, Resolver)}.
 33  */
 34 public interface ComboParameter {
 35 
 36     /**
 37      * A combo parameter can take the form:
 38      * <p>
 39      * #{MAJOR}
 40      * #{MAJOR.}
 41      * #{MAJOR.MINOR}
 42      * <p>
 43      * where MAJOR can be IDENTIFIER or IDENTIFIER[NUMERIC_INDEX]
 44      * and MINOR can be an identifier.
 45      */
 46     Pattern pattern = Pattern.compile("#\\{([A-Z_][A-Z0-9_]*(?:\\[\\d+\\])?)(?:\\.([A-Z0-9_]*))?\\}");
 47 
 48     /**
 49      * Entry point for the customizable replacement logic. Subclasses must implement this method to
 50      * specify how a given template hole should be expanded. An optional contextual argument is passed
 51      * in as parameter, to make expansion more flexible.
 52      */
 53     String expand(String optParameter);
 54 
 55     /**
 56      * Helper class for defining 'constant' combo parameters - i.e. parameters that always expand
 57      * as a given string value - regardless of the context.
 58      */
 59     class Constant<D> implements ComboParameter {
 60 
 61         D data;
 62 
 63         public Constant(D data) {
 64             this.data = data;
 65         }
 66 
 67         @Override
 68         public String expand(String _unused) {
 69             return String.valueOf(data);
 70         }
 71     }
 72 
 73     /**
 74      * Helper interface used to lookup parameters given a parameter name.
 75      */
 76     interface Resolver {
 77         ComboParameter lookup(String name);
 78     }
 79 
 80     /**
 81      * Main routine for replacing holes in a template string. Holes are repeatedly searches, their
 82      * corresponding parameters retrieved, and replaced through expansion; since an expansion can
 83      * lead to more holes, the process has to be applied until a fixed point is reached.
 84      */
 85     static String expandTemplate(String template, Resolver resolver) {
 86         CharSequence in = template;
 87         StringBuffer out = new StringBuffer();
 88         while (true) {
 89             boolean more = false;
 90             Matcher m = pattern.matcher(in);
 91             while (m.find()) {
 92                 String parameterName = m.group(1);
 93                 String minor = m.group(2);
 94                 ComboParameter parameter = resolver.lookup(parameterName);
 95                 if (parameter == null) {
 96                     throw new IllegalStateException("Unhandled parameter name " + parameterName);
 97                 }
 98 
 99                 String replacement = parameter.expand(minor);
100                 more |= pattern.matcher(replacement).find();
101                 m.appendReplacement(out, replacement);
102             }
103             m.appendTail(out);
104             if (!more)
105                 return out.toString();
106             else {
107                 in = out;
108                 out = new StringBuffer();
109             }
110         }
111     }
112 }