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