1 /****************************************************************************
3 ** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
43 * NOTE: This file is included by qxsdstatemachine_p.h
44 * if you need some includes, put them in qxsdstatemachine_p.h (outside of the namespace)
47 template <typename TransitionType
>
48 XsdStateMachine
<TransitionType
>::XsdStateMachine()
53 template <typename TransitionType
>
54 XsdStateMachine
<TransitionType
>::XsdStateMachine(const NamePool::Ptr
&namePool
)
55 : m_namePool(namePool
)
60 template <typename TransitionType
>
61 typename XsdStateMachine
<TransitionType
>::StateId XsdStateMachine
<TransitionType
>::addState(StateType type
)
64 // make sure we don't have two start states
65 if (type
== StartState
) {
66 QHashIterator
<StateId
, StateType
> it(m_states
);
67 while (it
.hasNext()) {
69 Q_ASSERT(it
.value() != StartState
&& it
.value() != StartEndState
);
74 // reserve new state id
75 const StateId id
= ++m_counter
;
76 m_states
.insert(id
, type
);
78 // if it is a start state, we make it to our current state
79 if (type
== StartState
|| type
== StartEndState
)
85 template <typename TransitionType
>
86 void XsdStateMachine
<TransitionType
>::addTransition(StateId start
, TransitionType transition
, StateId end
)
88 QHash
<TransitionType
, QVector
<StateId
> > &hash
= m_transitions
[start
];
89 QVector
<StateId
> &states
= hash
[transition
];
90 if (!states
.contains(end
))
94 template <typename TransitionType
>
95 void XsdStateMachine
<TransitionType
>::addEpsilonTransition(StateId start
, StateId end
)
97 QVector
<StateId
> &states
= m_epsilonTransitions
[start
];
101 template <typename TransitionType
>
102 void XsdStateMachine
<TransitionType
>::reset()
104 // reset the machine to the start state
105 QHashIterator
<StateId
, StateType
> it(m_states
);
106 while (it
.hasNext()) {
108 if (it
.value() == StartState
|| it
.value() == StartEndState
) {
109 m_currentState
= it
.key();
117 template <typename TransitionType
>
118 void XsdStateMachine
<TransitionType
>::clear()
121 m_transitions
.clear();
122 m_epsilonTransitions
.clear();
127 template <typename TransitionType
>
128 bool XsdStateMachine
<TransitionType
>::proceed(TransitionType transition
)
130 // check that we are not in an invalid state
131 if (!m_transitions
.contains(m_currentState
)) {
135 // fetch the transition entry for the current state
136 const QHash
<TransitionType
, QVector
<StateId
> > &entry
= m_transitions
[m_currentState
];
137 if (entry
.contains(transition
)) { // is there an transition for the given input?
138 m_currentState
= entry
.value(transition
).first();
139 m_lastTransition
= transition
;
146 template <typename TransitionType
>
147 QList
<TransitionType
> XsdStateMachine
<TransitionType
>::possibleTransitions() const
149 // check that we are not in an invalid state
150 if (!m_transitions
.contains(m_currentState
)) {
151 return QList
<TransitionType
>();
154 // fetch the transition entry for the current state
155 const QHash
<TransitionType
, QVector
<StateId
> > &entry
= m_transitions
[m_currentState
];
160 template <typename TransitionType
>
161 template <typename InputType
>
162 bool XsdStateMachine
<TransitionType
>::proceed(InputType input
)
164 // check that we are not in an invalid state
165 if (!m_transitions
.contains(m_currentState
)) {
169 // fetch the transition entry for the current state
170 const QHash
<TransitionType
, QVector
<StateId
> > &entry
= m_transitions
[m_currentState
];
171 QHashIterator
<TransitionType
, QVector
<StateId
> > it(entry
);
172 while (it
.hasNext()) {
174 if (inputEqualsTransition(input
, it
.key())) {
175 m_currentState
= it
.value().first();
176 m_lastTransition
= it
.key();
184 template <typename TransitionType
>
185 template <typename InputType
>
186 bool XsdStateMachine
<TransitionType
>::inputEqualsTransition(InputType input
, TransitionType transition
) const
191 template <typename TransitionType
>
192 bool XsdStateMachine
<TransitionType
>::inEndState() const
194 // check if current state is an end state
195 return (m_states
.value(m_currentState
) == StartEndState
|| m_states
.value(m_currentState
) == EndState
);
198 template <typename TransitionType
>
199 TransitionType XsdStateMachine
<TransitionType
>::lastTransition() const
201 return m_lastTransition
;
204 template <typename TransitionType
>
205 typename XsdStateMachine
<TransitionType
>::StateId XsdStateMachine
<TransitionType
>::startState() const
207 QHashIterator
<StateId
, StateType
> it(m_states
);
208 while (it
.hasNext()) {
210 if (it
.value() == StartState
|| it
.value() == StartEndState
)
214 Q_ASSERT(false); // should never be reached
218 template <typename TransitionType
>
219 QString XsdStateMachine
<TransitionType
>::transitionTypeToString(TransitionType type
) const
226 template <typename TransitionType
>
227 bool XsdStateMachine
<TransitionType
>::outputGraph(QIODevice
*device
, const QString
&graphName
) const
229 if (!device
->isOpen()) {
230 qWarning("device must be open for writing");
235 QTextStream
s(&graph
);
237 QHashIterator
<StateId
, QHash
<TransitionType
, QVector
<StateId
> > > it(m_transitions
);
238 QHashIterator
<StateId
, StateType
> it3(m_states
);
240 s
<< "digraph " << graphName
<< " {\n";
241 s
<< " mindist = 2.0\n";
244 while (it
.hasNext()) {
247 QHashIterator
<TransitionType
, QVector
<StateId
> > it2(it
.value());
248 while (it2
.hasNext()) {
250 for (int i
= 0; i
< it2
.value().count(); ++i
)
251 s
<< " " << it
.key() << " -> " << it2
.value().at(i
) << " [label=\"" << transitionTypeToString(it2
.key()) << "\"]\n";
255 QHashIterator
<StateId
, QVector
<StateId
> > it4(m_epsilonTransitions
);
256 while (it4
.hasNext()) {
259 const QVector
<StateId
> states
= it4
.value();
260 for (int i
= 0; i
< states
.count(); ++i
)
261 s
<< " " << it4
.key() << " -> " << states
.at(i
) << " [label=\"ε\"]\n";
265 while (it3
.hasNext()) {
269 if (it3
.value() == StartState
) {
270 style
= QLatin1String("shape=circle, style=filled, color=blue");
271 } else if (it3
.value() == StartEndState
) {
272 style
= QLatin1String("shape=doublecircle, style=filled, color=blue");
273 } else if (it3
.value() == InternalState
) {
274 style
= QLatin1String("shape=circle, style=filled, color=red");
275 } else if (it3
.value() == EndState
) {
276 style
= QLatin1String("shape=doublecircle, style=filled, color=green");
279 s
<< " " << it3
.key() << " [" << style
<< "]\n";
286 if (device
->write(graph
) == -1)
293 template <typename TransitionType
>
294 typename XsdStateMachine
<TransitionType
>::StateId XsdStateMachine
<TransitionType
>::dfaStateForNfaState(QSet
<StateId
> nfaState
,
295 QList
< QPair
<QSet
<StateId
>, StateId
> > &stateTable
,
296 XsdStateMachine
<TransitionType
> &dfa
) const
298 // check whether we have the given state in our lookup table
299 // already, in that case simply return it
300 for (int i
= 0; i
< stateTable
.count(); ++i
) {
301 if (stateTable
.at(i
).first
== nfaState
)
302 return stateTable
.at(i
).second
;
305 // check if the NFA state set contains a Start or End
306 // state, in that case our new DFA state will be a
307 // Start or End state as well
308 StateType type
= InternalState
;
309 QSetIterator
<StateId
> it(nfaState
);
310 bool hasStartState
= false;
311 bool hasEndState
= false;
312 while (it
.hasNext()) {
313 const StateId state
= it
.next();
314 if (m_states
.value(state
) == EndState
) {
316 } else if (m_states
.value(state
) == StartState
) {
317 hasStartState
= true;
322 type
= StartEndState
;
325 } else if (hasEndState
) {
329 // create the new DFA state
330 const StateId dfaState
= dfa
.addState(type
);
332 // add the new DFA state to the lookup table
333 stateTable
.append(qMakePair
<QSet
<StateId
>, StateId
>(nfaState
, dfaState
));
338 template <typename TransitionType
>
339 XsdStateMachine
<TransitionType
> XsdStateMachine
<TransitionType
>::toDFA() const
341 XsdStateMachine
<TransitionType
> dfa(m_namePool
);
343 QList
< QPair
< QSet
<StateId
>, StateId
> > table
;
344 QList
< QSet
<StateId
> > isMarked
;
346 // search the start state as the algorithm starts with it...
347 StateId startState
= -1;
348 QHashIterator
<StateId
, StateType
> stateTypeIt(m_states
);
349 while (stateTypeIt
.hasNext()) {
351 if (stateTypeIt
.value() == StartState
) {
352 startState
= stateTypeIt
.key();
356 Q_ASSERT(startState
!= -1);
358 // our list of state set that still have to be processed
359 QList
< QSet
<StateId
> > workStates
;
361 // add the start state to the list of to processed state sets
362 workStates
.append(epsilonClosure(QSet
<StateId
>() << startState
));
364 while (!workStates
.isEmpty()) { // as long as there are state sets to process left
366 // enqueue set of states
367 const QSet
<StateId
> states
= workStates
.takeFirst();
369 if (isMarked
.contains(states
)) // we processed this state set already
373 isMarked
.append(states
);
375 // select a list of all inputs that are possible for
377 QList
<TransitionType
> input
;
380 QSetIterator
<StateId
> it(states
);
381 while (it
.hasNext()) {
382 input
<< m_transitions
.value(it
.next()).keys();
386 // get the state in DFA that corresponds to the 'states' set in the NFA
387 const StateId dfaBegin
= dfaStateForNfaState(states
, table
, dfa
);
389 for (int i
= 0; i
< input
.count(); ++i
) { // for each possible input
390 // retrieve the states that can be reached from the 'states' set by the
391 // given input or by epsilon transition
392 const QSet
<StateId
> followStates
= epsilonClosure(move(states
, input
.at(i
)));
394 // get the state in DFA that corresponds to the 'followStates' set in the NFA
395 const StateId dfaEnd
= dfaStateForNfaState(followStates
, table
, dfa
);
397 // adds a new transition to the DFA that corresponds to the transitions between
398 // 'states' and 'followStates' in the NFA
399 dfa
.addTransition(dfaBegin
, input
.at(i
), dfaEnd
);
401 // add the 'followStates' to the list of to be processed state sets
402 workStates
.append(followStates
);
409 template <typename TransitionType
>
410 QHash
<typename XsdStateMachine
<TransitionType
>::StateId
, typename XsdStateMachine
<TransitionType
>::StateType
> XsdStateMachine
<TransitionType
>::states() const