1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 
18 package android.filterfw.io;
19 
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22 
23 /**
24  * @hide
25  */
26 public class PatternScanner {
27 
28     private String mInput;
29     private Pattern mIgnorePattern;
30     private int mOffset = 0;
31     private int mLineNo = 0;
32     private int mStartOfLine = 0;
33 
PatternScanner(String input)34     public PatternScanner(String input) {
35         mInput = input;
36     }
37 
PatternScanner(String input, Pattern ignorePattern)38     public PatternScanner(String input, Pattern ignorePattern) {
39         mInput = input;
40         mIgnorePattern = ignorePattern;
41         skip(mIgnorePattern);
42     }
43 
tryEat(Pattern pattern)44     public String tryEat(Pattern pattern) {
45         // Skip ignore pattern
46         if (mIgnorePattern != null) {
47             skip(mIgnorePattern);
48         }
49 
50         // Create the matcher
51         Matcher matcher = pattern.matcher(mInput);
52         matcher.region(mOffset, mInput.length());
53 
54         // Attempt to match
55         String result = null;
56         if (matcher.lookingAt()) {
57             updateLineCount(mOffset, matcher.end());
58             mOffset = matcher.end();
59             result = mInput.substring(matcher.start(), matcher.end());
60         }
61 
62         // Skip ignore pattern
63         if (result != null && mIgnorePattern != null) {
64             skip(mIgnorePattern);
65         }
66 
67         return result;
68     }
69 
eat(Pattern pattern, String tokenName)70     public String eat(Pattern pattern, String tokenName) {
71         String result = tryEat(pattern);
72         if (result == null) {
73             throw new RuntimeException(unexpectedTokenMessage(tokenName));
74         }
75         return result;
76     }
77 
peek(Pattern pattern)78     public boolean peek(Pattern pattern) {
79         // Skip ignore pattern
80         if (mIgnorePattern != null) {
81             skip(mIgnorePattern);
82         }
83 
84         // Create the matcher
85         Matcher matcher = pattern.matcher(mInput);
86         matcher.region(mOffset, mInput.length());
87 
88         // Attempt to match
89         return matcher.lookingAt();
90     }
91 
skip(Pattern pattern)92     public void skip(Pattern pattern) {
93         Matcher matcher = pattern.matcher(mInput);
94         matcher.region(mOffset, mInput.length());
95         if (matcher.lookingAt()) {
96             updateLineCount(mOffset, matcher.end());
97             mOffset = matcher.end();
98         }
99     }
100 
atEnd()101     public boolean atEnd() {
102         return mOffset >= mInput.length();
103     }
104 
lineNo()105     public int lineNo() {
106         return mLineNo;
107     }
108 
unexpectedTokenMessage(String tokenName)109     public String unexpectedTokenMessage(String tokenName) {
110         String line = mInput.substring(mStartOfLine, mOffset);
111         return "Unexpected token on line " + (mLineNo + 1) + " after '" + line + "' <- Expected " +
112                 tokenName + "!";
113     }
114 
updateLineCount(int start, int end)115     public void updateLineCount(int start, int end) {
116         for (int i = start; i < end; ++i) {
117             if (mInput.charAt(i) == '\n') {
118                 ++mLineNo;
119                 mStartOfLine = i + 1;
120             }
121         }
122     }
123 }
124