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 return switch (type) { 80 case UPPER -> "ASCII.UPPER"; 81 case LOWER -> "ASCII.LOWER"; 82 case DIGIT -> "ASCII.DIGIT"; 83 case SPACE -> "ASCII.SPACE"; 84 case PUNCT -> "ASCII.PUNCT"; 85 case CNTRL -> "ASCII.CNTRL"; 86 case BLANK -> "ASCII.BLANK"; 87 case UNDER -> "ASCII.UNDER"; 88 case ASCII -> "ASCII.ASCII"; 89 case ALPHA -> "ASCII.ALPHA"; 90 case ALNUM -> "ASCII.ALNUM"; 91 case GRAPH -> "ASCII.GRAPH"; 92 case WORD -> "ASCII.WORD"; 93 case XDIGIT -> "ASCII.XDIGIT"; 94 default -> "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 c) { 135 str = "Curly " + c.type + " " + toStringRange(c.cmin, c.cmax); 136 print(node, str, depth); 137 walk(c.atom, depth); 138 print("/Curly", depth); 139 } else if (node instanceof Pattern.GroupCurly gc) { 140 str = "GroupCurly " + gc.groupIndex / 2 + 141 ", " + gc.type + " " + toStringRange(gc.cmin, gc.cmax); 142 print(node, str, depth); 143 walk(gc.atom, depth); 144 print("/GroupCurly", depth); 145 } else if (node instanceof Pattern.GroupHead head) { 146 Pattern.GroupTail tail = head.tail; 147 print(head, "Group.head " + (tail.groupIndex / 2), depth); 148 walk(head.next, depth); 149 print(tail, "/Group.tail " + (tail.groupIndex / 2), depth); 150 node = tail; 151 } else if (node instanceof Pattern.GroupTail) { 152 return; // stopper 153 } else if (node instanceof Pattern.Ques) { 154 print(node, "Ques " + ((Pattern.Ques)node).type, depth); 155 walk(((Pattern.Ques)node).atom, depth); 156 print("/Ques", depth); 157 } else if (node instanceof Pattern.Branch b) { 158 print(b, name, depth); 159 int i = 0; 160 while (true) { 161 if (b.atoms[i] != null) { 162 walk(b.atoms[i], depth); 163 } else { 164 print(" (accepted)", depth); 165 } 166 if (++i == b.size) 167 break; 168 print("-branch.separator-", depth); 169 } 170 node = b.conn; 171 print(node, "/Branch", depth); 172 } else if (node instanceof Pattern.BranchConn) { 173 return; 174 } else if (node instanceof Pattern.CharProperty) { 175 str = pmap.get(((Pattern.CharProperty)node).predicate); 176 if (str == null) 177 str = toString(node); 178 else 179 str = "Single \"" + str + "\""; 180 print(node, str, depth); 181 } else if (node instanceof Pattern.SliceNode) { 182 str = name + " \"" + 183 toStringCPS(((Pattern.SliceNode)node).buffer) + "\""; 184 print(node, str, depth); 185 } else if (node instanceof Pattern.CharPropertyGreedy gcp) { 186 String pstr = pmap.get(gcp.predicate); 187 if (pstr == null) 188 pstr = gcp.predicate.toString(); 189 else 190 pstr = "Single \"" + pstr + "\""; 191 str = name + " " + pstr; 192 if (gcp.cmin == 0) 193 str += "*"; 194 else if (gcp.cmin == 1) 195 str += "+"; 196 else 197 str += "{" + gcp.cmin + ",}"; 198 print(node, str, depth); 199 } else if (node instanceof Pattern.BackRef) { 200 str = "GroupBackRef " + ((Pattern.BackRef)node).groupIndex / 2; 201 print(node, str, depth); 202 } else if (node instanceof Pattern.LastNode) { 203 print(node, "END", depth); 204 } else if (node == Pattern.accept) { 205 return; 206 } else { 207 print(node, name, depth); 208 } 209 node = node.next; 210 } 211 } 212 main(String[] args)213 public static void main(String[] args) { 214 Pattern p = Pattern.compile(args[0]); 215 System.out.println(" Pattern: " + p); 216 walk(p.root, 0); 217 } 218 } 219