• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LearnLib / automatalib / 13138848026

04 Feb 2025 02:53PM UTC coverage: 92.108% (+2.2%) from 89.877%
13138848026

push

github

mtf90
[maven-release-plugin] prepare release automatalib-0.12.0

16609 of 18032 relevant lines covered (92.11%)

1.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.41
/serialization/aut/src/main/java/net/automatalib/serialization/aut/InternalAUTParser.java
1
/* Copyright (C) 2013-2025 TU Dortmund University
2
 * This file is part of AutomataLib <https://automatalib.net>.
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
package net.automatalib.serialization.aut;
17

18
import java.io.BufferedReader;
19
import java.io.IOException;
20
import java.io.InputStream;
21
import java.util.HashMap;
22
import java.util.HashSet;
23
import java.util.Map;
24
import java.util.Set;
25
import java.util.function.Function;
26

27
import net.automatalib.alphabet.Alphabet;
28
import net.automatalib.alphabet.impl.Alphabets;
29
import net.automatalib.automaton.AutomatonCreator;
30
import net.automatalib.automaton.MutableAutomaton;
31
import net.automatalib.common.util.HashUtil;
32
import net.automatalib.common.util.IOUtil;
33
import net.automatalib.exception.FormatException;
34
import net.automatalib.serialization.InputModelData;
35
import net.automatalib.serialization.InputModelDeserializer;
36

37
class InternalAUTParser<I, T, A extends MutableAutomaton<Integer, I, T, ?, ?>>
38
        implements InputModelDeserializer<I, A> {
39

40
    private final Function<String, I> inputTransformer;
41
    private final AutomatonCreator<A, I> creator;
42
    private final Set<String> alphabetSymbols;
43
    private final Map<Integer, Map<String, Set<Integer>>> transitionMap;
44

45
    private int initialState;
46
    private int numStates;
47
    private char[] currentLineContent;
48
    private int currentLine;
49
    private int currentPos;
50

51
    InternalAUTParser(Function<String, I> inputTransformer, AutomatonCreator<A, I> creator) {
2✔
52
        this.inputTransformer = inputTransformer;
2✔
53
        this.creator = creator;
2✔
54
        this.alphabetSymbols = new HashSet<>();
2✔
55
        this.transitionMap = new HashMap<>();
2✔
56
    }
2✔
57

58
    @Override
59
    public InputModelData<I, A> readModel(InputStream is) throws IOException, FormatException {
60

61
        try (BufferedReader br = new BufferedReader(IOUtil.asNonClosingUTF8Reader(is))) {
2✔
62
            // parsing
63
            parseHeader(br);
2✔
64
            while (parseTransition(br)) {
2✔
65
                // consume transitions
66
            }
67

68
            // automaton construction
69
            final Map<String, I> inputMap = new HashMap<>(HashUtil.capacity(this.alphabetSymbols.size()));
2✔
70
            for (String s : this.alphabetSymbols) {
2✔
71
                inputMap.put(s, inputTransformer.apply(s));
2✔
72
            }
2✔
73
            final Alphabet<I> alphabet = Alphabets.fromCollection(inputMap.values());
2✔
74

75
            final A result = creator.createAutomaton(alphabet, numStates);
2✔
76

77
            for (int i = 0; i < numStates; i++) {
2✔
78
                result.addState();
2✔
79
            }
80

81
            for (Map.Entry<Integer, Map<String, Set<Integer>>> outgoing : transitionMap.entrySet()) {
2✔
82
                final Integer src = outgoing.getKey();
2✔
83
                for (Map.Entry<String, Set<Integer>> targets : outgoing.getValue().entrySet()) {
2✔
84
                    final String input = targets.getKey();
2✔
85
                    final Set<Integer> tgts = targets.getValue();
2✔
86
                    for (Integer tgt : tgts) {
2✔
87
                        result.addTransition(src, inputMap.get(input), tgt, null);
2✔
88
                    }
2✔
89
                }
2✔
90
            }
2✔
91
            result.setInitial(initialState, true);
2✔
92

93
            return new InputModelData<>(result, alphabet);
2✔
94
        } finally {
95
            // cleanup
96
            initialState = 0;
2✔
97
            numStates = 0;
2✔
98
            currentLineContent = null;
2✔
99
            currentLine = 0;
2✔
100
            currentPos = 0;
2✔
101
            alphabetSymbols.clear();
2✔
102
            transitionMap.clear();
2✔
103
        }
104
    }
105

106
    private void parseHeader(BufferedReader reader) throws IOException, FormatException {
107
        final String line = reader.readLine();
2✔
108

109
        if (line == null) {
2✔
110
            throw new FormatException(buildErrorMessage("Missing description"));
×
111
        }
112

113
        currentLineContent = line.toCharArray();
2✔
114
        currentPos = 0;
2✔
115

116
        shiftToNextNonWhitespace();
2✔
117
        verifyDesAndShift();
2✔
118
        verifyLBracketAndShift();
2✔
119
        initialState = parseNumberAndShift();
2✔
120
        verifyCommaAndShift();
2✔
121
        parseNumberAndShift(); // ignore number of transitions
2✔
122
        verifyCommaAndShift();
2✔
123
        numStates = parseNumberAndShift(); // store number of states
2✔
124
        if (numStates < 1) {
2✔
125
            throw new FormatException("Number of states must be >= 1");
2✔
126
        }
127
        verifyRBracketAndShift();
2✔
128
    }
2✔
129

130
    private boolean parseTransition(BufferedReader reader) throws IOException, FormatException {
131
        final String line = reader.readLine();
2✔
132

133
        if (line == null) {
2✔
134
            return false;
2✔
135
        }
136

137
        currentLineContent = line.toCharArray();
2✔
138
        currentLine++;
2✔
139
        currentPos = 0;
2✔
140

141
        final int start;
142
        final String label;
143
        final int dest;
144

145
        shiftToNextNonWhitespace();
2✔
146
        verifyLBracketAndShift();
2✔
147
        start = parseNumberAndShift();
2✔
148
        verifyCommaAndShift();
2✔
149
        label = parseLabelAndShift();
2✔
150
        verifyCommaAndShift();
2✔
151
        dest = parseNumberAndShift();
2✔
152
        verifyRBracketAndShift();
2✔
153

154
        alphabetSymbols.add(label);
2✔
155
        transitionMap.computeIfAbsent(start, k -> new HashMap<>())
2✔
156
                     .computeIfAbsent(label, k -> new HashSet<>())
2✔
157
                     .add(dest);
2✔
158

159
        return true;
2✔
160
    }
161

162
    private void verifyDesAndShift() throws FormatException {
163

164
        if (currentLineContent[currentPos] != 'd' || currentLineContent[currentPos + 1] != 'e' ||
2✔
165
            currentLineContent[currentPos + 2] != 's') {
166
            throw new FormatException(buildErrorMessage("Missing 'des' keyword"));
2✔
167
        }
168

169
        currentPos += 3;
2✔
170
        shiftToNextNonWhitespace();
2✔
171
    }
2✔
172

173
    private void verifyLBracketAndShift() throws FormatException {
174
        verifySymbolAndShift('(');
2✔
175
    }
2✔
176

177
    private void verifyRBracketAndShift() throws FormatException {
178
        verifySymbolAndShift(')');
2✔
179
    }
2✔
180

181
    private void verifyCommaAndShift() throws FormatException {
182
        verifySymbolAndShift(',');
2✔
183
    }
2✔
184

185
    private void verifySymbolAndShift(char symbol) throws FormatException {
186

187
        if (currentLineContent[currentPos] != symbol) {
2✔
188
            throw new FormatException(buildErrorMessage("Expected: " + symbol));
2✔
189
        }
190

191
        currentPos++;
2✔
192
        shiftToNextNonWhitespace();
2✔
193
    }
2✔
194

195
    private void shiftToNextNonWhitespace() {
196
        while (currentPos < currentLineContent.length && Character.isWhitespace(currentLineContent[currentPos])) {
2✔
197
            currentPos++;
2✔
198
        }
199
    }
2✔
200

201
    private int parseNumberAndShift() throws FormatException {
202

203
        final StringBuilder sb = new StringBuilder();
2✔
204

205
        char sym = currentLineContent[currentPos];
2✔
206

207
        while (Character.isDigit(sym)) {
2✔
208
            sb.append(sym);
2✔
209
            currentPos++;
2✔
210
            sym = currentLineContent[currentPos];
2✔
211
        }
212

213
        if (sb.length() == 0) {
2✔
214
            throw new FormatException(buildErrorMessage("Expected a positive number"));
2✔
215
        }
216

217
        // forward pointer
218
        shiftToNextNonWhitespace();
2✔
219
        return Integer.parseInt(sb.toString());
2✔
220
    }
221

222
    private String parseLabelAndShift() throws FormatException {
223

224
        if (currentLineContent[currentPos] == '"') {
2✔
225
            return parseQuotedLabelAndShift();
2✔
226
        } else {
227
            return parseNormalLabelAndShift();
2✔
228
        }
229
    }
230

231
    private String parseQuotedLabelAndShift() {
232
        int openingIndex = currentPos;
2✔
233
        int closingIndex = currentLineContent.length - 1;
2✔
234

235
        // find terminating "
236
        while (currentLineContent[closingIndex--] != '"') {
2✔
237
            // consume characters
238
        }
239

240
        // skip terminating " as well
241
        currentPos = closingIndex + 2;
2✔
242
        shiftToNextNonWhitespace();
2✔
243

244
        return new String(currentLineContent, openingIndex + 1, closingIndex - openingIndex);
2✔
245
    }
246

247
    private String parseNormalLabelAndShift() throws FormatException {
248

249
        final char firstChar = currentLineContent[currentPos];
2✔
250

251
        if (currentLineContent[currentPos] == '*') {
2✔
252
            currentPos++;
2✔
253
            shiftToNextNonWhitespace();
2✔
254
            return "*";
2✔
255
        } else if (Character.isLetter(firstChar)) {
2✔
256
            int startIdx = currentPos;
2✔
257

258
            while (isValidIdentifier()) {
2✔
259
                currentPos++;
2✔
260
            }
261

262
            int endIdx = currentPos;
2✔
263

264
            shiftToNextNonWhitespace();
2✔
265
            return new String(currentLineContent, startIdx, endIdx - startIdx);
2✔
266
        } else {
267
            throw new FormatException(buildErrorMessage("Invalid unquoted label"));
×
268
        }
269
    }
270

271
    private boolean isValidIdentifier() {
272
        final char currentChar = currentLineContent[currentPos];
2✔
273
        return Character.isLetterOrDigit(currentChar) || currentChar == '_';
2✔
274
    }
275

276
    private String buildErrorMessage(String desc) {
277
        return "In line " + currentLine + ", col " + currentPos + ": " + desc;
2✔
278
    }
279
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc