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

LearnLib / automatalib / 10248696670

05 Aug 2024 12:21PM UTC coverage: 90.741% (+0.7%) from 90.072%
10248696670

push

github

web-flow
Serialization Overhaul (#81)

* overhaul serizalition code

* unify access to Input(De)Serializers behind facades to leverage the default methods for various input/output channels
* drop implicit buffering/decompressing as this should be decided where the stream are constructed (user-land)
* remove/cleanup the (now) unused code

* taf cleanups

* fix wording

343 of 358 new or added lines in 33 files covered. (95.81%)

7 existing lines in 2 files now uncovered.

15994 of 17626 relevant lines covered (90.74%)

1.68 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-2024 TU Dortmund University
2
 * This file is part of AutomataLib, http://www.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, A extends MutableAutomaton<Integer, I, Integer, ?, ?>>
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

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

73
            final A result = creator.createAutomaton(alphabet, numStates);
2✔
74

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

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

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

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

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

111
        currentLineContent = line.toCharArray();
2✔
112
        currentPos = 0;
2✔
113

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

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

131
        if (line == null) {
2✔
132
            return false;
2✔
133
        }
134

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

139
        final int start;
140
        final String label;
141
        final int dest;
142

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

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

157
        return true;
2✔
158
    }
159

160
    private void verifyDesAndShift() throws FormatException {
161

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

167
        currentPos += 3;
2✔
168
        shiftToNextNonWhitespace();
2✔
169
    }
2✔
170

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

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

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

183
    private void verifySymbolAndShift(char symbol) throws FormatException {
184

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

189
        currentPos++;
2✔
190
        shiftToNextNonWhitespace();
2✔
191
    }
2✔
192

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

199
    private int parseNumberAndShift() throws FormatException {
200

201
        final StringBuilder sb = new StringBuilder();
2✔
202

203
        char sym = currentLineContent[currentPos];
2✔
204

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

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

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

220
    private String parseLabelAndShift() throws FormatException {
221

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

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

233
        // find terminating "
234
        while (currentLineContent[closingIndex--] != '"') {}
2✔
235

236
        // skip terminating " as well
237
        currentPos = closingIndex + 2;
2✔
238
        shiftToNextNonWhitespace();
2✔
239

240
        return new String(currentLineContent, openingIndex + 1, closingIndex - openingIndex);
2✔
241
    }
242

243
    private String parseNormalLabelAndShift() throws FormatException {
244

245
        final char firstChar = currentLineContent[currentPos];
2✔
246

247
        if (currentLineContent[currentPos] == '*') {
2✔
248
            currentPos++;
2✔
249
            shiftToNextNonWhitespace();
2✔
250
            return "*";
2✔
251
        } else if (Character.isLetter(firstChar)) {
2✔
252
            int startIdx = currentPos;
2✔
253

254
            while (isValidIdentifier()) {
2✔
255
                currentPos++;
2✔
256
            }
257

258
            int endIdx = currentPos;
2✔
259

260
            shiftToNextNonWhitespace();
2✔
261
            return new String(currentLineContent, startIdx, endIdx - startIdx);
2✔
262
        } else {
NEW
263
            throw new FormatException(buildErrorMessage("Invalid unquoted label"));
×
264
        }
265
    }
266

267
    private boolean isValidIdentifier() {
268
        final char currentChar = currentLineContent[currentPos];
2✔
269
        return Character.isLetterOrDigit(currentChar) || currentChar == '_';
2✔
270
    }
271

272
    private String buildErrorMessage(String desc) {
273
        return "In line " + currentLine + ", col " + currentPos + ": " + desc;
2✔
274
    }
275
}
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