FreeCAD

Форк
0
/
Range.cpp 
323 строки · 8.1 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name>             *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  is distributed in the hope that it will be useful,      *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
#include <boost/regex.hpp>
26
#include <cassert>
27
#include <sstream>
28
#include <string>
29
#include <cmath>
30
#include <regex>
31
#endif
32

33
#include <string_view>
34
#include <Base/Exception.h>
35
#include "Range.h"
36

37

38
using namespace App;
39

40
const int App::CellAddress::MAX_ROWS = 16384;
41
const int App::CellAddress::MAX_COLUMNS = 26 * 26 + 26;
42

43
namespace App {
44
// From a given cell address the '$' must be at within the first
45
// few characters
46
bool maybeAbsolute(std::string_view address)
47
{
48
    const int MAX_COLUMNS_LETTERS = 2;
49

50
    address = address.substr(0, MAX_COLUMNS_LETTERS + 1);
51
    return address.find("$") != std::string_view::npos;
52
}
53
}
54

55
Range::Range(const char * range, bool normalize)
56
{
57
    std::string from;
58
    std::string to;
59

60
    assert(range);
61

62
    if (!strchr(range, ':')) {
63
        from = range;
64
        to = range;
65
    }
66
    else {
67
        std::string s = range;
68
        from = s.substr(0, s.find(':'));
69
        to = s.substr(s.find(':') + 1);
70
    }
71

72
    CellAddress begin(from);
73
    CellAddress end(to);
74

75
    row_begin = begin.row();
76
    col_begin = begin.col();
77
    row_end = end.row();
78
    col_end = end.col();
79

80
    if (normalize) {
81
        this->normalize();
82
    }
83
    row_curr = row_begin;
84
    col_curr = col_begin;
85
}
86

87
Range::Range(int _row_begin, int _col_begin, int _row_end, int _col_end, bool normalize)
88
    : row_begin(_row_begin)
89
    , col_begin(_col_begin)
90
    , row_end(_row_end)
91
    , col_end(_col_end)
92
{
93
    if (normalize) {
94
        this->normalize();
95
    }
96
    row_curr = row_begin;
97
    col_curr = col_begin;
98
}
99

100
Range::Range(const CellAddress &from, const CellAddress &to, bool normalize)
101
    : row_begin(from.row())
102
    , col_begin(from.col())
103
    , row_end(to.row())
104
    , col_end(to.col())
105
{
106
    if (normalize) {
107
        this->normalize();
108
    }
109
    row_curr = row_begin;
110
    col_curr = col_begin;
111
}
112

113
void Range::normalize()
114
{
115
    if (row_begin > row_end) {
116
        std::swap(row_begin, row_end);
117
    }
118
    if (col_begin > col_end) {
119
        std::swap(col_begin, col_end);
120
    }
121
}
122

123
bool Range::next()
124
{
125
    if (row_curr < row_end) {
126
        row_curr++;
127

128
        return true;
129
    }
130
    if (col_curr < col_end) {
131
        if (row_curr == row_end + 1)
132
            return false;
133
        row_curr = row_begin;
134
        ++col_curr;
135
        return true;
136
    }
137
    return false;
138
}
139

140
/**
141
  * @brief Decode a row specification into a 0-based integer.
142
  *
143
  * @param rowstr Row specified as a string, with "1" being the first row.
144
  *
145
  * @returns The row.
146
  */
147

148
int App::decodeRow(const std::string &rowstr, bool silent)
149
{
150
    int row = validRow(rowstr);
151

152
    if (silent || row >= 0) {
153
        return row;
154
    }
155

156
    throw Base::IndexError("Invalid row specification.");
157
}
158

159
/**
160
 * Assumes well-formed input. A through ZZZ. 0-based output
161
 */
162
int columnStringToNum(const std::string &colstr) {
163
    double out {0};
164
    int pos {0};
165
    for (auto chr = colstr.crbegin(); chr != colstr.crend(); chr++){
166
        out += (*chr - 'A' + 1) * std::pow(26, pos++);
167
    }
168

169
    return static_cast<int>(out - 1);
170
}
171

172
/**
173
  * @brief Decode a column name string into a 0-based integer.
174
  *
175
  * @param colstr input string.
176
  *
177
  * @returns The column.
178
  *
179
  */
180

181
int App::decodeColumn( const std::string &colstr, bool silent )
182
{
183
    if (validColumn(colstr)) {
184
        return columnStringToNum(colstr);
185
    }
186

187
    if (silent) {
188
        return -1;
189
    }
190

191
    throw Base::IndexError("Invalid column specification");
192
}
193

194
/**
195
  * @brief Determine whether a row specification is valid or not.
196
  *
197
  * @param rowstr Row specified as a string, with "1" being the first row.
198
  *
199
  * @returns 0 or positive on success, -1 on error.
200
  */
201

202
int App::validRow(const std::string &rowstr)
203
{
204
    char * end;
205
    int i = strtol(rowstr.c_str(), &end, 10);
206

207
    if (i <=0 || i > CellAddress::MAX_ROWS || *end) {
208
        return -1;
209
    }
210

211
    return i - 1;
212
}
213

214
/**
215
  * @brief Determine if a string is a valid column specification.
216
  *
217
  * @param colstr input string.
218
  *
219
  * @returns true if valid, false if not.
220
  *
221
  */
222

223
bool App::validColumn( const std::string &colstr )
224
{
225
    return boost::regex_match(colstr, boost::regex("[A-Z]{1,3}"));
226
}
227

228
/**
229
  * @brief Convert a string address into integer \a row and \a column.
230
  * row and col are 0-based.
231
  *
232
  * This function will throw an exception if the specified \a address is invalid.
233
  *
234
  * @param strAddress Address to parse.
235
  *
236
  */
237

238
App::CellAddress App::stringToAddress(const char * strAddress, bool silent)
239
{
240
    assert(strAddress);
241

242
    static boost::regex e("(\\$?[A-Z]{1,2})(\\$?[0-9]{1,5})");
243
    boost::cmatch cm;
244

245
    if (boost::regex_match(strAddress, cm, e)) {
246
        bool absCol = (cm[1].first[0]=='$');
247
        std::string r,c;
248
        if (absCol) {
249
            c = std::string(cm[1].first+1,cm[1].second);
250
        }
251
        else {
252
            c = std::string(cm[1].first,cm[1].second);
253
        }
254

255
        bool absRow = (cm[2].first[0]=='$');
256
        if (absRow) {
257
            r = std::string(cm[2].first+1,cm[2].second);
258
        }
259
        else {
260
            r = std::string(cm[2].first,cm[2].second);
261
        }
262

263
        return CellAddress(decodeRow(r,silent), decodeColumn(c,silent), absRow, absCol);
264
    }
265
    else if (silent) {
266
        return CellAddress();
267
    }
268

269
    throw Base::RuntimeError("Invalid cell specifier.");
270
}
271

272
/**
273
  * @brief Convert given \a cell address into its string representation.
274
  *
275
  * @returns Address given as a string.
276
  */
277

278
std::string App::CellAddress::toString(Cell cell) const
279
{
280
    std::stringstream s;
281

282
    Base::Flags<Cell> flags(cell);
283
    if (flags.testFlag(Cell::ShowColumn)) {
284
        if (_absCol && flags.testFlag(Cell::Absolute)) {
285
            s << '$';
286
        }
287
        if (col() < 26) {
288
            s << static_cast<char>('A' + col());
289
        }
290
        else {
291
            int colnum = col() - 26;
292

293
            s << static_cast<char>('A' + (colnum / 26));
294
            s << static_cast<char>('A' + (colnum % 26));
295
        }
296
    }
297

298
    if (flags.testFlag(Cell::ShowRow)) {
299
        if (_absRow && flags.testFlag(Cell::Absolute)) {
300
            s << '$';
301
        }
302
        s << (row() + 1);
303
    }
304

305
    return s.str();
306
}
307

308
/*!
309
 * \brief App::CellAddress::parseAbsoluteAddress
310
 * \param address
311
 * If the passed string is a valid and absolute cell address it will be assigned to this instance.
312
 * \return True if it's an absolute cell address and false otherwise
313
 */
314
bool App::CellAddress::parseAbsoluteAddress(const char *address) {
315
    if (maybeAbsolute(address)) {
316
        CellAddress addr = stringToAddress(address, true);
317
        if (addr.isValid()) {
318
            *this = addr;
319
            return true;
320
        }
321
    }
322
    return false;
323
}
324

325

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.