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