1 /*
2  * Copyright (c) 2016, 2019, 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 java.util.regex;
27 
28 import java.util.HashMap;
29 import java.util.regex.Pattern.CharPredicate;
30 import static java.util.regex.ASCII.*;
31 
32 /**
33  * A utility class to print out the pattern node tree.
34  */
35 
36 class PrintPattern {
37 
38     private static HashMap<Pattern.Node, Integer> ids = new HashMap<>();
39 
print(Pattern.Node node, String text, int depth)40     private static void print(Pattern.Node node, String text, int depth) {
41         if (!ids.containsKey(node))
42             ids.put(node, ids.size());
43         System.out.printf("%6d:%" + (depth==0? "": depth<<1) + "s<%s>",
44                           ids.get(node), "", text);
45         if (ids.containsKey(node.next))
46             System.out.printf(" (=>%d)", ids.get(node.next));
47         System.out.printf("%n");
48     }
49 
print(String s, int depth)50     private static void print(String s, int depth) {
51         System.out.printf("       %" + (depth==0?"":depth<<1) + "s<%s>%n",
52                           "", s);
53     }
54 
toStringCPS(int[] cps)55     private static String toStringCPS(int[] cps) {
56         StringBuilder sb = new StringBuilder(cps.length);
57         for (int cp : cps)
58             sb.append(toStringCP(cp));
59         return sb.toString();
60     }
61 
toStringCP(int cp)62     private static String toStringCP(int cp) {
63         return (isPrint(cp) ? "" + (char)cp
64                             : "\\u" + Integer.toString(cp, 16));
65     }
66 
toStringRange(int min, int max)67     private static String toStringRange(int min, int max) {
68        if (max == Pattern.MAX_REPS) {
69            if (min == 0)
70                return " * ";
71            else if (min == 1)
72                return " + ";
73            return "{" + min + ", max}";
74        }
75        return "{" + min + ", " +  max + "}";
76     }
77 
toStringCtype(int type)78     private static String toStringCtype(int type) {
79         switch(type) {
80         case UPPER:  return "ASCII.UPPER";
81         case LOWER:  return "ASCII.LOWER";
82         case DIGIT:  return "ASCII.DIGIT";
83         case SPACE:  return "ASCII.SPACE";
84         case PUNCT:  return "ASCII.PUNCT";
85         case CNTRL:  return "ASCII.CNTRL";
86         case BLANK:  return "ASCII.BLANK";
87         case UNDER:  return "ASCII.UNDER";
88         case ASCII:  return "ASCII.ASCII";
89         case ALPHA:  return "ASCII.ALPHA";
90         case ALNUM:  return "ASCII.ALNUM";
91         case GRAPH:  return "ASCII.GRAPH";
92         case WORD:   return "ASCII.WORD";
93         case XDIGIT: return "ASCII.XDIGIT";
94         default: return "ASCII ?";
95         }
96     }
97 
toString(Pattern.Node node)98     private static String toString(Pattern.Node node) {
99         String name = node.getClass().getName();
100         return name.substring(name.lastIndexOf('$') + 1);
101     }
102 
103     static HashMap<CharPredicate, String> pmap;
104     static {
105         pmap = new HashMap<>();
Pattern.ALL()106         pmap.put(Pattern.ALL(), "All");
Pattern.DOT()107         pmap.put(Pattern.DOT(), "Dot");
Pattern.UNIXDOT()108         pmap.put(Pattern.UNIXDOT(), "UnixDot");
Pattern.VertWS()109         pmap.put(Pattern.VertWS(), "VertWS");
Pattern.HorizWS()110         pmap.put(Pattern.HorizWS(), "HorizWS");
111 
CharPredicates.ASCII_DIGIT()112         pmap.put(CharPredicates.ASCII_DIGIT(), "ASCII.DIGIT");
CharPredicates.ASCII_WORD()113         pmap.put(CharPredicates.ASCII_WORD(),  "ASCII.WORD");
CharPredicates.ASCII_SPACE()114         pmap.put(CharPredicates.ASCII_SPACE(), "ASCII.SPACE");
115     }
116 
walk(Pattern.Node node, int depth)117     static void walk(Pattern.Node node, int depth) {
118         depth++;
119         while(node != null) {
120             String name = toString(node);
121             String str;
122             if (node instanceof Pattern.Prolog) {
123                 print(node, name, depth);
124                 // print the loop here
125                 Pattern.Loop loop = ((Pattern.Prolog)node).loop;
126                 name = toString(loop);
127                 str = name + " " + toStringRange(loop.cmin, loop.cmax);
128                 print(loop, str, depth);
129                 walk(loop.body, depth);
130                 print("/" + name, depth);
131                 node = loop;
132             } else if (node instanceof Pattern.Loop) {
133                 return;  // stop here, body.next -> loop
134             } else if (node instanceof Pattern.Curly) {
135                 Pattern.Curly c = (Pattern.Curly)node;
136                 str = "Curly " + c.type + " " + toStringRange(c.cmin, c.cmax);
137                 print(node, str, depth);
138                 walk(c.atom, depth);
139                 print("/Curly", depth);
140             } else if (node instanceof Pattern.GroupCurly) {
141                 Pattern.GroupCurly gc = (Pattern.GroupCurly)node;
142                 str = "GroupCurly " + gc.groupIndex / 2 +
143                       ", " + gc.type + " " + toStringRange(gc.cmin, gc.cmax);
144                 print(node, str, depth);
145                 walk(gc.atom, depth);
146                 print("/GroupCurly", depth);
147             } else if (node instanceof Pattern.GroupHead) {
148                 Pattern.GroupHead head = (Pattern.GroupHead)node;
149                 Pattern.GroupTail tail = head.tail;
150                 print(head, "Group.head " + (tail.groupIndex / 2), depth);
151                 walk(head.next, depth);
152                 print(tail, "/Group.tail " + (tail.groupIndex / 2), depth);
153                 node = tail;
154             } else if (node instanceof Pattern.GroupTail) {
155                 return;  // stopper
156             } else if (node instanceof Pattern.Ques) {
157                 print(node, "Ques " + ((Pattern.Ques)node).type, depth);
158                 walk(((Pattern.Ques)node).atom, depth);
159                 print("/Ques", depth);
160             } else if (node instanceof Pattern.Branch) {
161                 Pattern.Branch b = (Pattern.Branch)node;
162                 print(b, name, depth);
163                 int i = 0;
164                 while (true) {
165                     if (b.atoms[i] != null) {
166                         walk(b.atoms[i], depth);
167                     } else {
168                         print("  (accepted)", depth);
169                     }
170                     if (++i == b.size)
171                         break;
172                     print("-branch.separator-", depth);
173                 }
174                 node = b.conn;
175                 print(node, "/Branch", depth);
176             } else if (node instanceof Pattern.BranchConn) {
177                 return;
178             } else if (node instanceof Pattern.CharProperty) {
179                 str = pmap.get(((Pattern.CharProperty)node).predicate);
180                 if (str == null)
181                     str = toString(node);
182                 else
183                     str = "Single \"" + str + "\"";
184                 print(node, str, depth);
185             } else if (node instanceof Pattern.SliceNode) {
186                 str = name + "  \"" +
187                       toStringCPS(((Pattern.SliceNode)node).buffer) + "\"";
188                 print(node, str, depth);
189             } else if (node instanceof Pattern.CharPropertyGreedy) {
190                 Pattern.CharPropertyGreedy gcp = (Pattern.CharPropertyGreedy)node;
191                 String pstr = pmap.get(gcp.predicate);
192                 if (pstr == null)
193                     pstr = gcp.predicate.toString();
194                 else
195                     pstr = "Single \"" + pstr + "\"";
196                 str = name + " " + pstr;
197                 if (gcp.cmin == 0)
198                     str += "*";
199                 else if (gcp.cmin == 1)
200                     str += "+";
201                 else
202                     str += "{" + gcp.cmin + ",}";
203                 print(node, str, depth);
204             } else if (node instanceof Pattern.BackRef) {
205                 str = "GroupBackRef " + ((Pattern.BackRef)node).groupIndex / 2;
206                 print(node, str, depth);
207             } else if (node instanceof Pattern.LastNode) {
208                 print(node, "END", depth);
209             } else if (node == Pattern.accept) {
210                 return;
211             } else {
212                 print(node, name, depth);
213             }
214             node = node.next;
215         }
216     }
217 
main(String[] args)218     public static void main(String[] args) {
219         Pattern p = Pattern.compile(args[0]);
220         System.out.println("   Pattern: " + p);
221         walk(p.root, 0);
222     }
223 }
224