summaryrefslogtreecommitdiffstats
path: root/kig/misc/argsparser.h
blob: 001d935966671853e8f55c7df4e59783807be9d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright (C)  2002  Dominique Devriese <devriese@kde.org>

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.

#ifndef KIG_MISC_ARGSPARSER_H
#define KIG_MISC_ARGSPARSER_H

#include "../objects/common.h"

#include <string>

class ObjectImpType;

/**
 * This class is meant to take care of checking the types of the
 * parents to ObjectCalcer's, and to put them in the correct order.
 * An ObjectType should construct an ArgsParser with a specification
 * of the arguments it wants.  This specification is given as an array
 * of ArgsParser::spec structs.  This struct contains a pointer to an
 * ObjectImpType ( which is the type you want the argument to have ),
 * a string ( which is an I18N_NOOP'd string describing what you will
 * be using the argument for ) and a boolean ( which says whether the
 * constructed object is by construction on the curve argument ( if
 * the constructed object is a point ), or whether the constructed
 * object is by construction through the point argument ( if the
 * constructed object is a curve ) ).
 *
 * An ObjectType using an ArgsParser to take care of the various
 * things that it can handle ( impRequirement, the sortArgs functions
 * and the isDefinedOnOrThrough stuff ), should inherit from
 * ArgsParserObjectType, which takes care of calling the ArgsParser
 * for these things...  It also allows you to use a convenient
 * ObjectConstructor for your type.
 *
 * E.g., let's see what CircleBCPType has for its arguments spec:
 * here's some code:
 * \code
 * static const ArgsParser::spec argsspecTranslation[] =
 * {
 *   { ObjectImp::stype(), I18N_NOOP("Translate this object"), false },
 *   { VectorImp::stype(), I18N_NOOP("Translate by this vector"), false }
 * };
 *
 * TranslatedType::TranslatedType()
 *   : ArgsParserObjectType( "Translation", argsspecTranslation, 2 )
 * {
 * }
 *
 * ObjectImp* TranslatedType::calc( const Args& args, const KigDocument& ) const
 * {
 *   if ( ! margsparser.checkArgs( args ) ) return new InvalidImp;
 *
 *   Coordinate dir = static_cast<const VectorImp*>( args[1] )->dir();
 *   Transformation t = Transformation::translation( dir );
 *
 *   return args[0]->transform( t );
 * }
 * \endcode
 *
 * As you can see above, the argsspec can be declared right in the
 * cpp-file.  The usetexts explain to the user what the argument in
 * question will be used for.  The boolean says that in this case, the
 * constructed object is not by construction on or through one of its
 * arguments. In the constructor, you simply call the
 * ArgsParserObjectType with the argsspec struct you defined, and the
 * number of arguments in the argsspec ( in this case 2 ).
 *
 * In the calc function, you can rely on the arguments already being
 * in the correct order ( the same order as you put them in in the
 * arguments spec.  You should use the checkArgs function to check if
 * all the arguments are valid, and if they aren't return a
 * InvalidImp.  All objects can always become invalid ( e.g. an
 * intersection point of two non-intersecting conics can become valid
 * again when the conics move ), and you should always check for this.
 *
 * An interesting to note here is that the first argument is of a more
 * general type than the second.  A VectorImp is *also* an ObjectImp.
 * In general, when this happens, you should put the more general type
 * first, as in general this produces the results that the user
 * expects.  I have no formal proof for this, just talking from
 * experience.  It might be that you experience different things, but
 * unless you're sure of the results, put the more general type first.
 *
 * This class uses a pretty basic algorithm for doing the parsing (
 * e.g. if a match fails in one order, it does not try a different
 * order, which could perhaps be necessary in the case of having more
 * general argument types in the same argument spec ).  However, the
 * current algorithm works in all the situation where I've tested it,
 * and I don't feel the need to change it.  Feel free to do so if you
 * like, but even if you do, I'm not sure if I will include it in
 * mainline Kig.
 */
class ArgsParser
{
public:
  /**
   * this are some enum values that we return from some functions.
   */
  enum { Invalid = 0, Valid = 1, Complete = 2 };
  struct spec { const ObjectImpType* type; std::string usetext; std::string selectstat; bool onOrThrough;};
private:
  /**
   * the args spec..
   */
  std::vector<spec> margs;

  spec findSpec( const ObjectImp* o, const Args& parents ) const;
public:
  ArgsParser( const struct spec* args, int n );
  ArgsParser( const std::vector<spec>& args );
  ArgsParser();
  ~ArgsParser();

  void initialize( const std::vector<spec>& args );
  void initialize( const struct spec* args, int n );

  /**
   * returns a new ArgsParser that wants the same args, except for the
   * ones of the given type..
   */
  ArgsParser without( const ObjectImpType* type ) const;
  // checks if os matches the argument list this parser should parse..
  int check( const Args& os ) const;
  int check( const std::vector<ObjectCalcer*>& os ) const;
  /**
   * returns the usetext for the argument that o would be used for,
   * if sel were used as parents..
   * \p o should be in \p sel ...
   */
  std::string usetext( const ObjectImp* o, const Args& sel ) const;

  /**
   * returns the select statement for the next selectable argument
   * when the given args are selected.
   */
  std::string selectStatement( const Args& sel ) const;

  // this reorders the objects or args so that they are in the same
  // order as the requested arguments..
  Args parse( const Args& os ) const;
  std::vector<ObjectCalcer*> parse( const std::vector<ObjectCalcer*>& os ) const;

  /**
   * returns the minimal ObjectImp ID that \p o needs to inherit in order
   * to be useful..  \p o should be part of \p parents .
   */
  const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const;

  /**
   * Supposing that \p parents would be given as parents, this function
   * returns whether the returned ObjectImp will be, by construction,
   * on \p o ( if \p o is a curve ), or through \p o ( if \p o is a point ).
   */
  bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const;

  // Checks the args according to this args specification.  If the
  // objects should never have occurred, then an assertion failure
  // will happen, if one of the args is invalid, then false will be
  // returned, if all is fine, then true is returned..
  // assert that the objects are of the right types, and in the right
  // order as what would be returned by parse( os )..  If minobjects
  // is provided, then not all objects are needed, and it is enough if
  // at least minobjects are available..  Use this for object types
  // that can calc a temporary example object using less than the
  // required args.  These args need to be at the end of argsspec +
  // anyobjsspec.  If minobjects is not provided, then it is assumed
  // that all args are necessary.
  bool checkArgs( const std::vector<ObjectCalcer*>& os ) const;
  bool checkArgs( const std::vector<ObjectCalcer*>& os, uint minobjects ) const;
  bool checkArgs( const Args& os ) const;
  bool checkArgs( const Args& os, uint minobjects ) const;
};

#endif