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