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