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