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}