1 /*
2  * Copyright (c) 2013, 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 package tools.javac.combo;
24 
25 import java.util.Map;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28 
29 /**
30  * A template into which tags of the form {@code #\{KEY\}} or
31  * {@code #\{KEY.SUBKEY\}} can be expanded.
32  */
33 public interface Template {
expand(String selector)34     String expand(String selector);
35 
36     interface Resolver {
lookup(String key)37         public Template lookup(String key);
38     }
39 
40     public static class Behavior {
41         /* Looks for expandable keys.  An expandable key can take the form:
42          *   #{MAJOR}
43          *   #{MAJOR.}
44          *   #{MAJOR.MINOR}
45          * where MAJOR can be IDENTIFIER or IDENTIFIER[NUMERIC_INDEX]
46          * and MINOR can be an identifier.
47          *
48          * The ability to have an empty minor is provided on the
49          * assumption that some tests that can be written with this
50          * will find it useful to make a distinction akin to
51          * distinguishing F from F(), where F is a function pointer,
52          * and also cases of #{FOO.#{BAR}}, where BAR expands to an
53          * empty string.
54          *
55          * However, this being a general-purpose framework, the exact
56          * use is left up to the test writers.
57          */
58         private static final Pattern pattern = Pattern.compile("#\\{([A-Z_][A-Z0-9_]*(?:\\[\\d+\\])?)(?:\\.([A-Z0-9_]*))?\\}");
59 
expandTemplate(String template, final Map<String, Template> vars)60         public static String expandTemplate(String template, final Map<String, Template> vars) {
61             return expandTemplate(template, new MapResolver(vars));
62         }
63 
expandTemplate(String template, Resolver res)64         public static String expandTemplate(String template, Resolver res) {
65             CharSequence in = template;
66             StringBuffer out = new StringBuffer();
67             while (true) {
68                 boolean more = false;
69                 Matcher m = pattern.matcher(in);
70                 while (m.find()) {
71                     String major = m.group(1);
72                     String minor = m.group(2);
73                     Template key = res.lookup(major);
74                     if (key == null)
75                         throw new IllegalStateException("Unknown major key " + major);
76 
77                     String replacement = key.expand(minor == null ? "" : minor);
78                     more |= pattern.matcher(replacement).find();
79                     m.appendReplacement(out, replacement);
80                 }
81                 m.appendTail(out);
82                 if (!more)
83                     return out.toString();
84                 else {
85                     in = out;
86                     out = new StringBuffer();
87                 }
88             }
89         }
90 
91     }
92 }
93 
94 class MapResolver implements Template.Resolver {
95     private final Map<String, Template> vars;
96 
MapResolver(Map<String, Template> vars)97     public MapResolver(Map<String, Template> vars) {this.vars = vars;}
98 
lookup(String key)99     public Template lookup(String key) {
100         return vars.get(key);
101     }
102 }
103 
104 class ChainedResolver implements Template.Resolver {
105     private final Template.Resolver upstreamResolver, thisResolver;
106 
ChainedResolver(Template.Resolver upstreamResolver, Template.Resolver thisResolver)107     public ChainedResolver(Template.Resolver upstreamResolver, Template.Resolver thisResolver) {
108         this.upstreamResolver = upstreamResolver;
109         this.thisResolver = thisResolver;
110     }
111 
getUpstreamResolver()112     public Template.Resolver getUpstreamResolver() {
113         return upstreamResolver;
114     }
115 
116     @Override
lookup(String key)117     public Template lookup(String key) {
118         Template result = thisResolver.lookup(key);
119         if (result == null)
120             result = upstreamResolver.lookup(key);
121         return result;
122     }
123 }
124