1 // SPDX-FileCopyrightText: 2002 Dominique Devriese <devriese@kde.org>
2
3 // SPDX-License-Identifier: GPL-2.0-or-later
4
5 #include "object_imp_factory.h"
6
7 #include "object_imp.h"
8 #include "bogus_imp.h"
9 #include "circle_imp.h"
10 #include "conic_imp.h"
11 #include "cubic_imp.h"
12 #include "line_imp.h"
13 #include "locus_imp.h"
14 #include "other_imp.h"
15 #include "point_imp.h"
16 #include "text_imp.h"
17
18 #include "../misc/coordinate.h"
19
20 #include <qdom.h>
21
instance()22 const ObjectImpFactory* ObjectImpFactory::instance()
23 {
24 static const ObjectImpFactory t;
25 return &t;
26 }
27
ObjectImpFactory()28 ObjectImpFactory::ObjectImpFactory()
29 {
30 }
31
~ObjectImpFactory()32 ObjectImpFactory::~ObjectImpFactory()
33 {
34 }
35
addXYElements(const Coordinate & c,QDomElement & parent,QDomDocument & doc)36 static void addXYElements( const Coordinate& c, QDomElement& parent, QDomDocument& doc )
37 {
38 QDomElement xe = doc.createElement( QStringLiteral("x") );
39 xe.appendChild(
40 doc.createTextNode(
41 QString::number( c.x ) ) );
42 parent.appendChild( xe );
43 QDomElement ye = doc.createElement( QStringLiteral("y") );
44 ye.appendChild(
45 doc.createTextNode(
46 QString::number( c.y ) ) );
47 parent.appendChild( ye );
48 }
49
addDoubleElement(const char * name,double d,QDomElement & parent,QDomDocument & doc)50 static void addDoubleElement( const char* name, double d, QDomElement& parent, QDomDocument& doc )
51 {
52 QDomElement e = doc.createElement( name );
53 e.appendChild( doc.createTextNode( QString::number( d ) ) );
54 parent.appendChild( e );
55 }
56
addCoordinateElement(const char * name,const Coordinate & d,QDomElement & p,QDomDocument & doc)57 static void addCoordinateElement( const char* name, const Coordinate& d, QDomElement& p, QDomDocument& doc )
58 {
59 QDomElement e = doc.createElement( name );
60 addXYElements( d, e, doc );
61 p.appendChild( e );
62 }
63
serialize(const ObjectImp & d,QDomElement & parent,QDomDocument & doc) const64 QString ObjectImpFactory::serialize( const ObjectImp& d, QDomElement& parent,
65 QDomDocument& doc ) const
66 {
67 if( d.inherits( IntImp::stype() ) )
68 {
69 parent.appendChild(
70 doc.createTextNode(
71 QString::number( static_cast<const IntImp&>( d ).data() ) ) );
72 return QStringLiteral( "int" );
73 }
74 else if ( d.inherits( DoubleImp::stype() ) )
75 {
76 parent.appendChild(
77 doc.createTextNode(
78 QString::number( static_cast<const DoubleImp&>( d ).data() ) ) );
79 return QStringLiteral( "double" );
80 }
81 else if( d.inherits( StringImp::stype() ) )
82 {
83 parent.appendChild(
84 doc.createTextNode(
85 static_cast<const StringImp&>( d ).data() ) );
86 return QStringLiteral( "string" );
87 }
88 else if ( d.inherits( TestResultImp::stype() ) )
89 {
90 assert( false );
91 parent.appendChild(
92 doc.createTextNode(
93 static_cast<const TestResultImp&>( d ).data() ) );
94 return QStringLiteral( "testresult" );
95 }
96 else if( d.inherits( HierarchyImp::stype() ) )
97 {
98 static_cast<const HierarchyImp&>( d ).data().serialize( parent, doc );
99 return QStringLiteral( "hierarchy" );
100 }
101 else if ( d.inherits( TransformationImp::stype() ) )
102 {
103 const Transformation& trans = static_cast<const TransformationImp&>( d ).data();
104
105 QDomElement matrixe = doc.createElement( QStringLiteral("matrix") );
106 for ( int i = 0; i < 3; ++i )
107 {
108 for ( int j = 0; j < 3; ++j )
109 {
110 QDomElement elel = doc.createElement( QStringLiteral("element") );
111 elel.setAttribute( QStringLiteral("row"), QString::number( i ) );
112 elel.setAttribute( QStringLiteral("column"), QString::number( j ) );
113 elel.appendChild( doc.createTextNode( QString::number( trans.data( i, j ) ) ) );
114 matrixe.appendChild( elel );
115 };
116 }
117 parent.appendChild( matrixe );
118
119 QDomElement homothetye = doc.createElement( QStringLiteral("homothetic") );
120 const char* ishomothety = trans.isHomothetic() ? "true" : "false";
121 homothetye.appendChild( doc.createTextNode( ishomothety ) );
122 parent.appendChild( homothetye );
123
124 return QStringLiteral( "transformation" );
125 }
126 else if( d.inherits( AbstractLineImp::stype() ) )
127 {
128 LineData l = static_cast<const AbstractLineImp&>( d ).data();
129 addCoordinateElement( "a", l.a, parent, doc );
130 addCoordinateElement( "b", l.b, parent, doc );
131 if( d.inherits( SegmentImp::stype() ) )
132 return QStringLiteral( "segment" );
133 else if( d.inherits( RayImp::stype() ) )
134 return QStringLiteral( "ray" );
135 else return QStringLiteral( "line" );
136 }
137 else if( d.inherits( PointImp::stype() ) )
138 {
139 addXYElements( static_cast<const PointImp&>( d ).coordinate(),
140 parent, doc );
141 return QStringLiteral( "point" );
142 }
143 else if( d.inherits( TextImp::stype() ) )
144 {
145 QString text = static_cast<const TextImp&>( d ).text();
146 parent.appendChild(
147 doc.createTextNode( text ) );
148 return QStringLiteral( "text" );
149 }
150 else if( d.inherits( AngleImp::stype() ) )
151 {
152 addDoubleElement( "size", static_cast<const AngleImp&>( d ).size(), parent, doc );
153 return QStringLiteral( "angle" );
154 }
155 else if ( d.inherits( ArcImp::stype() ) )
156 {
157 const ArcImp& a = static_cast<const ArcImp&>( d );
158 addCoordinateElement( "center", a.center(), parent, doc );
159 addDoubleElement( "radius", a.radius(), parent, doc );
160 addDoubleElement( "startangle", a.startAngle(), parent, doc );
161 addDoubleElement( "angle", a.angle(), parent, doc );
162 return QStringLiteral( "arc" );
163 }
164 else if( d.inherits( VectorImp::stype() ) )
165 {
166 Coordinate dir = static_cast<const VectorImp&>( d ).dir();
167 addXYElements( dir, parent, doc );
168 return QStringLiteral( "vector" );
169 }
170 else if( d.inherits( LocusImp::stype() ) )
171 {
172 const LocusImp& locus = static_cast<const LocusImp&>( d );
173
174 // serialize the curve..
175 QDomElement curve = doc.createElement( QStringLiteral("curve") );
176 const CurveImp& curveimp = *locus.curve();
177 QString type = serialize( curveimp, curve, doc );
178 curve.setAttribute( QStringLiteral("type"), type );
179 parent.appendChild( curve );
180
181 // serialize the hierarchy..
182 QDomElement hier = doc.createElement( QStringLiteral("calculation") );
183 locus.hierarchy().serialize( hier, doc );
184 parent.appendChild( hier );
185
186 return QStringLiteral( "locus" );
187 }
188 else if( d.inherits( CircleImp::stype() ) )
189 {
190 const CircleImp& c = static_cast<const CircleImp&>( d );
191 addCoordinateElement( "center", c.center(), parent, doc );
192 addDoubleElement( "radius", c.radius(), parent, doc );
193 return QStringLiteral( "circle" );
194 }
195 else if( d.inherits( ConicImp::stype() ) )
196 {
197 const ConicPolarData data = static_cast<const ConicImp&>( d ).polarData();
198 addCoordinateElement( "focus1", data.focus1, parent, doc );
199 addDoubleElement( "pdimen", data.pdimen, parent, doc );
200 addDoubleElement( "ecostheta0", data.ecostheta0, parent, doc );
201 addDoubleElement( "esintheta0", data.esintheta0, parent, doc );
202 return QStringLiteral( "conic" );
203 }
204 else if( d.inherits( CubicImp::stype() ) )
205 {
206 const CubicCartesianData data = static_cast<const CubicImp&>( d ).data();
207 QDomElement coeffs = doc.createElement( QStringLiteral("coefficients") );
208 addDoubleElement( "a000", data.coeffs[0], coeffs, doc );
209 addDoubleElement( "a001", data.coeffs[1], coeffs, doc );
210 addDoubleElement( "a002", data.coeffs[2], coeffs, doc );
211 addDoubleElement( "a011", data.coeffs[3], coeffs, doc );
212 addDoubleElement( "a012", data.coeffs[4], coeffs, doc );
213 addDoubleElement( "a022", data.coeffs[5], coeffs, doc );
214 addDoubleElement( "a111", data.coeffs[6], coeffs, doc );
215 addDoubleElement( "a112", data.coeffs[7], coeffs, doc );
216 addDoubleElement( "a122", data.coeffs[8], coeffs, doc );
217 addDoubleElement( "a222", data.coeffs[9], coeffs, doc );
218 parent.appendChild( coeffs );
219 return QStringLiteral( "cubic" );
220 }
221 assert( false );
222 return QString();
223 }
224
readXYElements(const QDomElement & e,bool & ok)225 static Coordinate readXYElements( const QDomElement& e, bool& ok )
226 {
227 double x, y;
228 ok = true;
229 QDomElement xe = e.firstChild().toElement();
230 if ( xe.isNull() || xe.tagName() != QLatin1String("x") )
231 {
232 ok = false;
233 return Coordinate();
234 }
235 else x = xe.text().toDouble( &ok );
236
237 QDomElement ye = xe.nextSibling().toElement();
238 if ( ye.isNull() || ye.tagName() != QLatin1String("y") )
239 {
240 ok = false;
241 return Coordinate();
242 }
243 else y = ye.text().toDouble( &ok );
244
245 return Coordinate( x, y );
246 }
247
readCoordinateElement(const QDomNode & n,bool & ok,const char * tagname)248 static Coordinate readCoordinateElement( const QDomNode& n, bool& ok,
249 const char* tagname )
250 {
251 QDomElement e = n.toElement();
252 if ( e.isNull() || e.tagName() != tagname )
253 {
254 ok = false;
255 Coordinate ret;
256 return ret;
257 }
258 return readXYElements( e, ok );
259 }
260
readDoubleElement(const QDomNode & n,bool & ok,const char * tagname)261 static double readDoubleElement( const QDomNode& n, bool& ok,
262 const char* tagname )
263 {
264 QDomElement e = n.toElement();
265 if ( e.isNull() || e.tagName() != tagname )
266 {
267 ok = false;
268 return 0.;
269 };
270 return e.text().toDouble( &ok );
271 }
272
deserialize(const QString & type,const QDomElement & parent,QString & error) const273 ObjectImp* ObjectImpFactory::deserialize( const QString& type,
274 const QDomElement& parent,
275 QString& error ) const
276 {
277 #define KIG_GENERIC_PARSE_ERROR \
278 { \
279 error = i18n( "An error was encountered at line %1 in file %2.", \
280 __LINE__, __FILE__ ); \
281 return 0; \
282 }
283
284 bool ok = true;
285 if ( type == QLatin1String("int") )
286 {
287 int ret = parent.text().toInt( &ok );
288 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
289 return new IntImp( ret );
290 }
291 else if ( type == QLatin1String("double") )
292 {
293 double ret = parent.text().toDouble( &ok );
294 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
295 return new DoubleImp( ret );
296 }
297 else if ( type == QLatin1String("string") )
298 {
299 return new StringImp( parent.text() );
300 }
301 else if ( type == QLatin1String("testresult") )
302 {
303 // should never get here, at least for new save files!
304 return new TestResultImp( true, parent.text() );
305 }
306 else if ( type == QLatin1String("hierarchy") )
307 {
308 ObjectHierarchy* hier = ObjectHierarchy::buildSafeObjectHierarchy( parent, error );
309 if ( ! hier ) return 0;
310 HierarchyImp* imp = new HierarchyImp( *hier );
311 delete hier;
312 return imp;
313 }
314 else if ( type == QLatin1String("transformation") )
315 {
316 double data[3][3];
317 bool homothetic = false;
318 for ( QDomElement childe = parent.firstChild().toElement();
319 ! childe.isNull(); childe = childe.nextSibling().toElement() )
320 {
321 if ( childe.tagName() == QLatin1String("matrix") )
322 {
323 for ( QDomElement elel = childe.firstChild().toElement();
324 ! elel.isNull(); elel = elel.nextSibling().toElement() )
325 {
326 if ( elel.tagName() != QLatin1String("element") ) KIG_GENERIC_PARSE_ERROR;
327 bool ok = true;
328 int row = elel.attribute( QStringLiteral("row") ).toInt( &ok );
329 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
330 int column = elel.attribute( QStringLiteral("column") ).toInt( &ok );
331 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
332 data[row][column] = elel.text().toDouble( &ok );
333 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
334 };
335 }
336 else if ( childe.tagName() == QLatin1String("homothetic") )
337 {
338 homothetic = childe.text() == QLatin1String("true");
339 }
340 else continue;
341 };
342 Transformation trans( data, homothetic );
343 return new TransformationImp( trans );
344 }
345 else if ( type == QLatin1String("point") )
346 {
347 Coordinate ret = readXYElements( parent, ok );
348 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
349 return new PointImp( ret );
350 }
351 else if ( type == QLatin1String("line") || type == QLatin1String("segment") || type == QLatin1String("ray") )
352 {
353 QDomNode n = parent.firstChild();
354 Coordinate a = readCoordinateElement( n, ok, "a" );
355 if ( !ok ) KIG_GENERIC_PARSE_ERROR;
356 n = n.nextSibling();
357 Coordinate b = readCoordinateElement( n, ok, "b" );
358 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
359 if ( type == QLatin1String("line") ) return new LineImp( a, b );
360 else if ( type == QLatin1String("segment") ) return new SegmentImp( a, b );
361 else return new RayImp( a, b );
362 }
363 else if( type == QLatin1String("angle") )
364 {
365 double size = readDoubleElement( parent.firstChild(), ok, "size" );
366 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
367
368 //TODO figure out how to know if this should be marked as a right angle
369 return new AngleImp( Coordinate(), 0, size, false );
370 }
371 else if ( type == QLatin1String("arc") )
372 {
373 QDomNode n = parent.firstChild();
374 Coordinate center = readCoordinateElement( n, ok, "center" );
375 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
376 n = n.nextSibling();
377 double radius = readDoubleElement( n, ok, "radius" );
378 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
379 n = n.nextSibling();
380 double startangle = readDoubleElement( n, ok, "startangle" );
381 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
382 n = n.nextSibling();
383 double angle = readDoubleElement( n, ok, "angle" );
384 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
385 return new ArcImp( center, radius, startangle, angle );
386 }
387 else if( type == QLatin1String("vector") )
388 {
389 Coordinate dir = readXYElements( parent, ok );
390 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
391 return new VectorImp( Coordinate(), dir );
392 }
393 else if( type == QLatin1String("locus") )
394 {
395 QDomElement curvee = parent.firstChild().toElement();
396 if ( curvee.isNull() || curvee.tagName() != QLatin1String("curve") ) KIG_GENERIC_PARSE_ERROR;
397 QString type = curvee.attribute( QStringLiteral("type") );
398 ObjectImp* oi = deserialize( type, curvee, error );
399 if ( ! oi || ! oi->inherits( CurveImp::stype() ) ) KIG_GENERIC_PARSE_ERROR;
400 //CurveImp* curvei = static_cast<CurveImp*>( oi );
401
402 QDomElement hiere = curvee.nextSibling().toElement();
403 if ( hiere.isNull() || hiere.tagName() != QLatin1String("calculation") ) KIG_GENERIC_PARSE_ERROR;
404 assert( false ); // TODO
405 // return new LocusImp( curvei, hier );
406 }
407 else if( type == QLatin1String("circle") )
408 {
409 QDomNode n = parent.firstChild();
410 Coordinate center = readCoordinateElement( n, ok, "center" );
411 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
412
413 n = n.nextSibling();
414 double radius = readDoubleElement( n, ok, "radius" );
415 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
416
417 return new CircleImp( center, radius );
418 }
419 else if( type == QLatin1String("conic") )
420 {
421 QDomNode n = parent.firstChild();
422 Coordinate focus1 = readCoordinateElement( n, ok, "focus1" );
423 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
424
425 n = n.nextSibling();
426 double pdimen = readDoubleElement( n, ok, "pdimen" );
427 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
428
429 n = n.nextSibling();
430 double ecostheta0 = readDoubleElement( n, ok, "ecostheta0" );
431 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
432
433 n = n.nextSibling();
434 double esintheta0 = readDoubleElement( n, ok, "esintheta0" );
435 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
436
437 return new ConicImpPolar(
438 ConicPolarData( focus1, pdimen, ecostheta0, esintheta0 ) );
439 }
440 else if( type == QLatin1String("cubic") )
441 {
442 QDomElement coeffse = parent.firstChild().toElement();
443 if ( coeffse.isNull() || coeffse.tagName() != QLatin1String("coefficients") )
444 KIG_GENERIC_PARSE_ERROR;
445
446 QDomNode n = coeffse.firstChild();
447 double a000 = readDoubleElement( n, ok, "a000" );
448 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
449
450 n = n.nextSibling();
451 double a001 = readDoubleElement( n, ok, "a001" );
452 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
453
454 n = n.nextSibling();
455 double a002 = readDoubleElement( n, ok, "a002" );
456 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
457
458 n = n.nextSibling();
459 double a011 = readDoubleElement( n, ok, "a011" );
460 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
461
462 n = n.nextSibling();
463 double a012 = readDoubleElement( n, ok, "a012" );
464 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
465
466 n = n.nextSibling();
467 double a022 = readDoubleElement( n, ok, "a022" );
468 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
469
470 n = n.nextSibling();
471 double a111 = readDoubleElement( n, ok, "a111" );
472 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
473
474 n = n.nextSibling();
475 double a112 = readDoubleElement( n, ok, "a112" );
476 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
477
478 n = n.nextSibling();
479 double a122 = readDoubleElement( n, ok, "a112" );
480 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
481
482 n = n.nextSibling();
483 double a222 = readDoubleElement( n, ok, "a222" );
484 if ( ! ok ) KIG_GENERIC_PARSE_ERROR;
485
486 return new CubicImp( CubicCartesianData( a000, a001, a002,
487 a011, a012, a022,
488 a111, a112, a122,
489 a222 ) );
490 }
491
492 error = i18n( "This Kig file uses an object of type \"%1\", "
493 "which this Kig version does not support."
494 "Perhaps you have compiled Kig without support "
495 "for this object type,"
496 "or perhaps you are using an older Kig version.", type );
497 return 0;
498 }
499
500