001/* 002 * Copyright (C) 2009-2011 Mathias Doenitz 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.parboiled; 018 019import org.parboiled.common.Preconditions; 020import org.parboiled.common.StringUtils; 021import org.parboiled.matchers.ActionMatcher; 022import org.parboiled.matchers.AnyMatcher; 023import org.parboiled.matchers.AnyOfMatcher; 024import org.parboiled.matchers.CharIgnoreCaseMatcher; 025import org.parboiled.matchers.CharMatcher; 026import org.parboiled.matchers.CharRangeMatcher; 027import org.parboiled.matchers.CustomMatcher; 028import org.parboiled.matchers.EmptyMatcher; 029import org.parboiled.matchers.FirstOfMatcher; 030import org.parboiled.matchers.FirstOfStringsMatcher; 031import org.parboiled.matchers.Matcher; 032import org.parboiled.matchers.MemoMismatchesMatcher; 033import org.parboiled.matchers.NothingMatcher; 034import org.parboiled.matchers.OneOrMoreMatcher; 035import org.parboiled.matchers.OptionalMatcher; 036import org.parboiled.matchers.ProxyMatcher; 037import org.parboiled.matchers.SequenceMatcher; 038import org.parboiled.matchers.StringMatcher; 039import org.parboiled.matchers.TestMatcher; 040import org.parboiled.matchers.TestNotMatcher; 041import org.parboiled.matchers.VarFramingMatcher; 042import org.parboiled.matchers.ZeroOrMoreMatcher; 043import org.parboiled.matchervisitors.MatcherVisitor; 044import org.parboiled.matchers.ActionMatcher; 045import org.parboiled.matchers.AnyMatcher; 046import org.parboiled.matchers.AnyOfMatcher; 047import org.parboiled.matchers.CharIgnoreCaseMatcher; 048import org.parboiled.matchers.CharMatcher; 049import org.parboiled.matchers.CharRangeMatcher; 050import org.parboiled.matchers.CustomMatcher; 051import org.parboiled.matchers.EmptyMatcher; 052import org.parboiled.matchers.FirstOfMatcher; 053import org.parboiled.matchers.FirstOfStringsMatcher; 054import org.parboiled.matchers.Matcher; 055import org.parboiled.matchers.MemoMismatchesMatcher; 056import org.parboiled.matchers.NothingMatcher; 057import org.parboiled.matchers.OneOrMoreMatcher; 058import org.parboiled.matchers.OptionalMatcher; 059import org.parboiled.matchers.ProxyMatcher; 060import org.parboiled.matchers.SequenceMatcher; 061import org.parboiled.matchers.StringMatcher; 062import org.parboiled.matchers.TestMatcher; 063import org.parboiled.matchers.TestNotMatcher; 064import org.parboiled.matchers.VarFramingMatcher; 065import org.parboiled.matchers.ZeroOrMoreMatcher; 066import org.parboiled.matchervisitors.MatcherVisitor; 067 068import java.util.ArrayList; 069import java.util.Collections; 070import java.util.HashSet; 071import java.util.List; 072import java.util.Set; 073 074public class ParserStatistics implements MatcherVisitor<ParserStatistics> { 075 076 private final Matcher root; 077 private int totalRules; 078 private final Set<AnyMatcher> anyMatchers = new HashSet<AnyMatcher>(); 079 private final Set<CharIgnoreCaseMatcher> charIgnoreCaseMatchers = new HashSet<CharIgnoreCaseMatcher>(); 080 private final Set<CharMatcher> charMatchers = new HashSet<CharMatcher>(); 081 private final Set<CustomMatcher> customMatchers = new HashSet<CustomMatcher>(); 082 private final Set<CharRangeMatcher> charRangeMatchers = new HashSet<CharRangeMatcher>(); 083 private final Set<AnyOfMatcher> anyOfMatchers = new HashSet<AnyOfMatcher>(); 084 private final Set<EmptyMatcher> emptyMatchers = new HashSet<EmptyMatcher>(); 085 private final Set<FirstOfMatcher> firstOfMatchers = new HashSet<FirstOfMatcher>(); 086 private final Set<FirstOfStringsMatcher> firstOfStringMatchers = new HashSet<FirstOfStringsMatcher>(); 087 private final Set<NothingMatcher> nothingMatchers = new HashSet<NothingMatcher>(); 088 private final Set<OneOrMoreMatcher> oneOrMoreMatchers = new HashSet<OneOrMoreMatcher>(); 089 private final Set<OptionalMatcher> optionalMatchers = new HashSet<OptionalMatcher>(); 090 private final Set<SequenceMatcher> sequenceMatchers = new HashSet<SequenceMatcher>(); 091 private final Set<StringMatcher> stringMatchers = new HashSet<StringMatcher>(); 092 private final Set<TestMatcher> testMatchers = new HashSet<TestMatcher>(); 093 private final Set<TestNotMatcher> testNotMatchers = new HashSet<TestNotMatcher>(); 094 private final Set<ZeroOrMoreMatcher> zeroOrMoreMatchers = new HashSet<ZeroOrMoreMatcher>(); 095 096 private final Set<Action> actions = new HashSet<Action>(); 097 private final Set<Class<?>> actionClasses = new HashSet<Class<?>>(); 098 private final Set<ProxyMatcher> proxyMatchers = new HashSet<ProxyMatcher>(); 099 private final Set<VarFramingMatcher> varFramingMatchers = new HashSet<VarFramingMatcher>(); 100 private final Set<MemoMismatchesMatcher> memoMismatchesMatchers = new HashSet<MemoMismatchesMatcher>(); 101 102 @SuppressWarnings({"unchecked"}) 103 public static ParserStatistics generateFor(Rule rule) { 104 Preconditions.checkArgNotNull(rule, "rule"); 105 Matcher matcher = (Matcher) rule; 106 return matcher.accept(new ParserStatistics(matcher)); 107 } 108 109 private ParserStatistics(Matcher root) { 110 this.root = root; 111 countSpecials(root); 112 } 113 114 public Rule getRootRule() { 115 return root; 116 } 117 118 public int getTotalRules() { 119 return totalRules; 120 } 121 122 public Set<AnyMatcher> getAnyMatchers() { 123 return anyMatchers; 124 } 125 126 public Set<CharIgnoreCaseMatcher> getCharIgnoreCaseMatchers() { 127 return charIgnoreCaseMatchers; 128 } 129 130 public Set<CharMatcher> getCharMatchers() { 131 return charMatchers; 132 } 133 134 public Set<CustomMatcher> getCustomMatchers() { 135 return customMatchers; 136 } 137 138 public Set<CharRangeMatcher> getCharRangeMatchers() { 139 return charRangeMatchers; 140 } 141 142 public Set<AnyOfMatcher> getAnyOfMatchers() { 143 return anyOfMatchers; 144 } 145 146 public Set<EmptyMatcher> getEmptyMatchers() { 147 return emptyMatchers; 148 } 149 150 public Set<FirstOfMatcher> getFirstOfMatchers() { 151 return firstOfMatchers; 152 } 153 154 public Set<FirstOfStringsMatcher> getFirstOfStringMatchers() { 155 return firstOfStringMatchers; 156 } 157 158 public Set<MemoMismatchesMatcher> getMemoMismatchesMatchers() { 159 return memoMismatchesMatchers; 160 } 161 162 public Set<NothingMatcher> getNothingMatchers() { 163 return nothingMatchers; 164 } 165 166 public Set<OneOrMoreMatcher> getOneOrMoreMatchers() { 167 return oneOrMoreMatchers; 168 } 169 170 public Set<OptionalMatcher> getOptionalMatchers() { 171 return optionalMatchers; 172 } 173 174 public Set<SequenceMatcher> getSequenceMatchers() { 175 return sequenceMatchers; 176 } 177 178 public Set<StringMatcher> getStringMatchers() { 179 return stringMatchers; 180 } 181 182 public Set<TestMatcher> getTestMatchers() { 183 return testMatchers; 184 } 185 186 public Set<TestNotMatcher> getTestNotMatchers() { 187 return testNotMatchers; 188 } 189 190 public Set<ZeroOrMoreMatcher> getZeroOrMoreMatchers() { 191 return zeroOrMoreMatchers; 192 } 193 194 public Set<Action> getActions() { 195 return actions; 196 } 197 198 public Set<Class<?>> getActionClasses() { 199 return actionClasses; 200 } 201 202 public Set<ProxyMatcher> getProxyMatchers() { 203 return proxyMatchers; 204 } 205 206 public Set<VarFramingMatcher> getVarFramingMatchers() { 207 return varFramingMatchers; 208 } 209 210 // MatcherVisitor interface 211 212 public ParserStatistics visit(ActionMatcher matcher) { 213 if (!actions.contains(matcher.action)) { 214 totalRules++; 215 actions.add(matcher.action); 216 actionClasses.add(matcher.action.getClass()); 217 } 218 return this; 219 } 220 221 public ParserStatistics visit(AnyMatcher matcher) { 222 return visit(matcher, anyMatchers); 223 } 224 225 public ParserStatistics visit(CharIgnoreCaseMatcher matcher) { 226 return visit(matcher, charIgnoreCaseMatchers); 227 } 228 229 public ParserStatistics visit(CharMatcher matcher) { 230 return visit(matcher, charMatchers); 231 } 232 233 public ParserStatistics visit(CustomMatcher matcher) { 234 return visit(matcher, customMatchers); 235 } 236 237 public ParserStatistics visit(CharRangeMatcher matcher) { 238 return visit(matcher, charRangeMatchers); 239 } 240 241 public ParserStatistics visit(AnyOfMatcher matcher) { 242 return visit(matcher, anyOfMatchers); 243 } 244 245 public ParserStatistics visit(EmptyMatcher matcher) { 246 return visit(matcher, emptyMatchers); 247 } 248 249 public ParserStatistics visit(FirstOfMatcher matcher) { 250 return matcher instanceof FirstOfStringsMatcher ? 251 visit((FirstOfStringsMatcher)matcher, firstOfStringMatchers) : 252 visit(matcher, firstOfMatchers); 253 } 254 255 public ParserStatistics visit(NothingMatcher matcher) { 256 return visit(matcher, nothingMatchers); 257 } 258 259 public ParserStatistics visit(OneOrMoreMatcher matcher) { 260 return visit(matcher, oneOrMoreMatchers); 261 } 262 263 public ParserStatistics visit(OptionalMatcher matcher) { 264 return visit(matcher, optionalMatchers); 265 } 266 267 public ParserStatistics visit(SequenceMatcher matcher) { 268 return matcher instanceof StringMatcher ? 269 visit((StringMatcher)matcher, stringMatchers) : 270 visit(matcher, sequenceMatchers); 271 } 272 273 public ParserStatistics visit(TestMatcher matcher) { 274 return visit(matcher, testMatchers); 275 } 276 277 public ParserStatistics visit(TestNotMatcher matcher) { 278 return visit(matcher, testNotMatchers); 279 } 280 281 public ParserStatistics visit(ZeroOrMoreMatcher matcher) { 282 return visit(matcher, zeroOrMoreMatchers); 283 } 284 285 private <M extends Matcher> ParserStatistics visit(M matcher, Set<M> set) { 286 if (!set.contains(matcher)) { 287 totalRules++; 288 set.add(matcher); 289 for (Matcher child : matcher.getChildren()) { 290 countSpecials(child); 291 child.accept(this); 292 } 293 } 294 return this; 295 } 296 297 private void countSpecials(Matcher matcher) { 298 if (matcher instanceof ProxyMatcher) { 299 proxyMatchers.add((ProxyMatcher) matcher); 300 } else if (matcher instanceof VarFramingMatcher) { 301 varFramingMatchers.add((VarFramingMatcher) matcher); 302 } else if (matcher instanceof MemoMismatchesMatcher) { 303 memoMismatchesMatchers.add((MemoMismatchesMatcher) matcher); 304 } 305 } 306 307 @Override 308 public String toString() { 309 return new StringBuilder("Parser statistics for rule '").append(root).append("':\n") 310 .append(" Total rules : ").append(totalRules).append('\n') 311 .append(" Actions : ").append(actions.size()).append('\n') 312 .append(" Any : ").append(anyMatchers.size()).append('\n') 313 .append(" CharIgnoreCase: ").append(charIgnoreCaseMatchers.size()).append('\n') 314 .append(" Char : ").append(charMatchers.size()).append('\n') 315 .append(" Custom : ").append(customMatchers.size()).append('\n') 316 .append(" CharRange : ").append(charRangeMatchers.size()).append('\n') 317 .append(" AnyOf : ").append(anyOfMatchers.size()).append('\n') 318 .append(" Empty : ").append(emptyMatchers.size()).append('\n') 319 .append(" FirstOf : ").append(firstOfMatchers.size()).append('\n') 320 .append(" FirstOfStrings: ").append(firstOfStringMatchers.size()).append('\n') 321 .append(" Nothing : ").append(nothingMatchers.size()).append('\n') 322 .append(" OneOrMore : ").append(oneOrMoreMatchers.size()).append('\n') 323 .append(" Optional : ").append(optionalMatchers.size()).append('\n') 324 .append(" Sequence : ").append(sequenceMatchers.size()).append('\n') 325 .append(" String : ").append(stringMatchers.size()).append('\n') 326 .append(" Test : ").append(testMatchers.size()).append('\n') 327 .append(" TestNot : ").append(testNotMatchers.size()).append('\n') 328 .append(" ZeroOrMore : ").append(zeroOrMoreMatchers.size()).append('\n') 329 .append('\n') 330 .append(" Action Classes : ").append(actionClasses.size()).append('\n') 331 .append(" ProxyMatchers : ").append(proxyMatchers.size()).append('\n') 332 .append(" VarFramingMatchers: ").append(varFramingMatchers.size()).append('\n') 333 .append("MemoMismatchesMatchers: ").append(memoMismatchesMatchers.size()).append('\n') 334 .toString(); 335 } 336 337 public String printActionClassInstances() { 338 StringBuilder sb = new StringBuilder("Action classes and their instances for rule '") 339 .append(root).append("':\n"); 340 341 for (String line : printActionClassLines()) { 342 sb.append(" ").append(line).append('\n'); 343 } 344 return sb.toString(); 345 } 346 347 private List<String> printActionClassLines() { 348 List<String> lines = new ArrayList<String>(); 349 int anonymous = 0; 350 for (Class<?> actionClass : actionClasses) { 351 String name = actionClass.getSimpleName(); 352 if (StringUtils.isEmpty(name)) { 353 anonymous++; 354 } else { 355 lines.add(name + " : " + StringUtils.join(printActionClassInstances(actionClass), ", ")); 356 } 357 } 358 Collections.sort(lines); 359 if (anonymous > 0) lines.add("and " + anonymous + " anonymous instance(s)"); 360 return lines; 361 } 362 363 private List<String> printActionClassInstances(Class<?> actionClass) { 364 List<String> actionNames = new ArrayList<String>(); 365 for (Action action : actions) { 366 if (action.getClass().equals(actionClass)) { 367 actionNames.add(action.toString()); 368 } 369 } 370 Collections.sort(actionNames); 371 return actionNames; 372 } 373 374}