1 /***************************************************************************
2 Copyright (C) 2002-2009 Robby Stephenson <robby@periapsis.org>
3 ***************************************************************************/
4
5 /***************************************************************************
6 * *
7 * This program is free software; you can redistribute it and/or *
8 * modify it under the terms of the GNU General Public License as *
9 * published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) version 3 or any later version *
11 * accepted by the membership of KDE e.V. (or its successor approved *
12 * by the membership of KDE e.V.), which shall act as a proxy *
13 * defined in Section 14 of version 3 of the license. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
22 * *
23 ***************************************************************************/
24
25 #include "isbnvalidator.h"
26 #include "upcvalidator.h"
27
28 #include <QStringList>
29 #include <QRegularExpression>
30
31 using Tellico::ISBNValidator;
32
33 //static
isbn10(QString isbn13)34 QString ISBNValidator::isbn10(QString isbn13) {
35 QString original = isbn13;
36 isbn13.remove(QLatin1Char('-'));
37 if(isbn13.length() == 10) {
38 fixup10(isbn13);
39 return isbn13;
40 }
41 if(!isbn13.startsWith(QStringLiteral("978"))) {
42 return original;
43 }
44 if(isbn13.length() < 13) {
45 fixup10(isbn13);
46 return isbn13;
47 }
48 isbn13 = isbn13.mid(3);
49 // remove checksum
50 isbn13.truncate(isbn13.length()-1);
51 // add new checksum
52 isbn13 += checkSum10(isbn13);
53 fixup10(isbn13);
54 return isbn13;
55 }
56
isbn13(QString isbn10)57 QString ISBNValidator::isbn13(QString isbn10) {
58 isbn10.remove(QLatin1Char('-'));
59 if(isbn10.length() < 10) {
60 return isbn10;
61 }
62 if(isbn10.length() > 10) {
63 // assume it's already an isbn13 value
64 fixup13(isbn10);
65 return isbn10;
66 }
67 // remove checksum
68 isbn10.truncate(isbn10.length()-1);
69 // begins with 978
70 isbn10.prepend(QStringLiteral("978"));
71 // add new checksum
72 isbn10 += checkSum13(isbn10);
73 fixup13(isbn10);
74 return isbn10;
75 }
76
listDifference(const QStringList & list1_,const QStringList & list2_)77 QStringList ISBNValidator::listDifference(const QStringList& list1_, const QStringList& list2_) {
78 ISBNComparison comp;
79
80 QStringList notFound;
81 foreach(const QString& value1, list1_) {
82 bool found = false;
83 foreach(const QString& value2, list2_) {
84 if(comp(value1, value2)) {
85 found = true;
86 break;
87 }
88 }
89 if(!found) {
90 notFound.append(value1);
91 }
92 }
93 return notFound;
94 }
95
cleanValue(QString isbn)96 QString ISBNValidator::cleanValue(QString isbn) {
97 static const QRegularExpression badChars(QStringLiteral("[^xX0123456789]"));
98 isbn.remove(badChars);
99 return isbn;
100 }
101
ISBNValidator(QObject * parent_)102 ISBNValidator::ISBNValidator(QObject* parent_)
103 : QValidator(parent_) {
104 }
105
validate(QString & input_,int & pos_) const106 QValidator::State ISBNValidator::validate(QString& input_, int& pos_) const {
107 // check if it's a cuecat first
108 State catState = CueCat::decode(input_);
109 if(catState != Invalid) {
110 pos_ = input_.length();
111 return catState;
112 }
113
114 if(input_.startsWith(QStringLiteral("978")) ||
115 input_.startsWith(QStringLiteral("979"))) {
116 return validate13(input_, pos_);
117 } else {
118 return validate10(input_, pos_);
119 }
120 }
121
fixup(QString & input_) const122 void ISBNValidator::fixup(QString& input_) const {
123 staticFixup(input_);
124 }
125
staticFixup(QString & input_)126 void ISBNValidator::staticFixup(QString& input_) {
127 if((input_.startsWith(QStringLiteral("978"))
128 || input_.startsWith(QStringLiteral("979")))
129 && input_.count(QRegularExpression(QStringLiteral("\\d"))) > 10) {
130 fixup13(input_);
131 } else {
132 fixup10(input_);
133 }
134 }
135
validate10(QString & input_,int & pos_) const136 QValidator::State ISBNValidator::validate10(QString& input_, int& pos_) const {
137 int len = input_.length();
138 /*
139 // Don't do this since the hyphens may be in the wrong place, can't put that in a regexp
140 if(isbn.exactMatch(input_) // put the exactMatch() first since I use matchedLength() later
141 && (len == 12 || len == 13)
142 && input_[len-1] == checkSum(input_)) {
143 return QValidator::Acceptable;
144 }
145 */
146 // two easy invalid cases are too many hyphens and the 'X' not in the last position
147 if(input_.count(QLatin1Char('-')) > 3
148 || input_.count(QLatin1Char('X'), Qt::CaseInsensitive) > 1
149 || (input_.indexOf(QLatin1Char('X'), 0, Qt::CaseInsensitive) != -1 && input_[len-1].toUpper() != QLatin1Char('X'))) {
150 return QValidator::Invalid;
151 }
152
153 // remember if the cursor is at the end
154 bool atEnd = (pos_ == static_cast<int>(len));
155
156 // fix the case where the user attempts to delete a character from a non-checksum
157 // position; the solution is to delete the checksum, but only if it's X
158 if(!atEnd && input_[len-1].toUpper() == QLatin1Char('X')) {
159 input_.truncate(len-1);
160 --len;
161 }
162
163 // fix the case where the user attempts to delete the checksum; the
164 // solution is to delete the last digit as well
165 static const QRegularExpression digit(QStringLiteral("\\d"));
166 if(atEnd && input_.count(digit) == 9 && input_[len-1] == QLatin1Char('-')) {
167 input_.truncate(len-2);
168 pos_ -= 2;
169 }
170
171 // now fixup the hyphens and maybe add a checksum
172 fixup10(input_);
173 len = input_.length(); // might have changed in fixup()
174 if(atEnd) {
175 pos_ = len;
176 }
177
178 // first check to see if it's a "perfect" ISBN
179 // A perfect ISBN has 9 digits plus either an 'X' or another digit
180 // A perfect ISBN may have 2 or 3 hyphens
181 // The final digit or 'X' is the correct check sum
182 static const QRegularExpression isbn(QStringLiteral("^(\\d-?){9,11}-[\\dX]$"));
183 if(isbn.match(input_).hasMatch() && (len == 12 || len == 13)) {
184 return QValidator::Acceptable;
185 } else {
186 return QValidator::Intermediate;
187 }
188 }
189
validate13(QString & input_,int & pos_) const190 QValidator::State ISBNValidator::validate13(QString& input_, int& pos_) const {
191 int len = input_.length();
192
193 const uint countX = input_.count(QLatin1Char('X'), Qt::CaseInsensitive);
194 // two easy invalid cases are too many hyphens or 'X'
195 if(input_.count(QLatin1Char('-')) > 4 || countX > 1) {
196 return QValidator::Invalid;
197 }
198
199 // now, it's not certain that we're getting a EAN-13,
200 // it could be a ISBN-10 from Nigeria or Indonesia
201 if(countX > 0 && (input_[len-1].toUpper() != QLatin1Char('X') || len > 13)) {
202 return QValidator::Invalid;
203 }
204
205 // remember if the cursor is at the end
206 bool atEnd = (pos_ == len);
207
208 // fix the case where the user attempts to delete a character from a non-checksum
209 // position; the solution is to delete the checksum, but only if it's X
210 if(!atEnd && input_[len-1].toUpper() == QLatin1Char('X')) {
211 input_.truncate(len-1);
212 --len;
213 }
214
215 // fix the case where the user attempts to delete the checksum; the
216 // solution is to delete the last digit as well
217 static const QRegularExpression digit(QStringLiteral("\\d"));
218 const uint countN = input_.count(digit);
219 if(atEnd && (countN == 12 || countN == 9) && input_[len-1] == QLatin1Char('-')) {
220 input_.truncate(len-2);
221 pos_ -= 2;
222 }
223
224 // now fixup the hyphens and maybe add a checksum
225 if(countN > 10) {
226 fixup13(input_);
227 } else {
228 fixup10(input_);
229 }
230
231 len = input_.length(); // might have changed in fixup()
232 if(atEnd) {
233 pos_ = len;
234 }
235
236 // first check to see if it's a "perfect" ISBN13
237 // A perfect ISBN13 has 13 digits
238 // A perfect ISBN13 may have 3 or 4 hyphens
239 // The final digit is the correct check sum
240 static const QRegularExpression isbn(QStringLiteral("^(\\d-?){13,17}$"));
241 if(isbn.match(input_).hasMatch()) {
242 return QValidator::Acceptable;
243 } else {
244 return QValidator::Intermediate;
245 }
246 }
247
fixup10(QString & input_)248 void ISBNValidator::fixup10(QString& input_) {
249 if(input_.isEmpty()) {
250 return;
251 }
252
253 //replace "x" with "X"
254 input_.replace(QLatin1Char('x'), QLatin1Char('X'));
255
256 // remove invalid chars
257 static const QRegularExpression badChars(QStringLiteral("[^\\d\\-X]"));
258 input_.remove(badChars);
259
260 // special case for EAN values that start with 978 or 979. That's the case
261 // for things like barcode readers that essentially 'type' the string at
262 // once. The simulated typing has already caused the input to be normalized,
263 // so strip that off, as well as the generated checksum. Then continue as normal.
264 // If someone were to input a regular 978- or 979- ISBN _including_ the
265 // checksum, it will be regarded as barcode input and the input will be stripped accordingly.
266 // I consider the likelihood that someone wants to input an EAN to be higher than someone
267 // using a Nigerian ISBN and not noticing that the checksum gets added automatically.
268 if(input_.length() > 12
269 && (input_.startsWith(QStringLiteral("978"))
270 || input_.startsWith(QStringLiteral("979")))) {
271 // Strip the first 3 characters (the invalid publisher)
272 // input_ = input_.right(input_.length() - 3);
273 }
274
275 // hyphen placement for some languages publishers is well-defined
276 // remove all hyphens, and insert them ourselves
277 // some countries have ill-defined second hyphen positions, and if
278 // the user inserts one, then be sure to put it back
279
280 // Find the first hyphen. If there is none,
281 // input_.indexOf('-') returns -1 and hyphen1_position = -1
282 int hyphen1_position = input_.indexOf(QLatin1Char('-'));
283
284 // Find the second one. If none, hyphen2_position = -2
285 int hyphen2_position = input_.indexOf(QLatin1Char('-'), hyphen1_position+1) - 1;
286
287 // The second hyphen can not be in the last character
288 if(hyphen2_position >= 9) {
289 hyphen2_position = 0;
290 }
291
292 const bool hyphenAtEnd = input_.endsWith(QLatin1Char('-'));
293
294 // Remove all existing hyphens. We will insert ours.
295 input_.remove(QLatin1Char('-'));
296 // the only place that 'X' can be is last spot
297 for(int xpos = input_.indexOf(QLatin1Char('X')); xpos > -1; xpos = input_.indexOf(QLatin1Char('X'), xpos+1)) {
298 if(xpos < 9) { // remove if not 10th char
299 input_.remove(xpos, 1);
300 --xpos;
301 }
302 }
303 input_.truncate(10);
304
305 // If we can find it, add the checksum
306 // but only if not started with 978 or 979
307 if(input_.length() > 8
308 && !input_.startsWith(QStringLiteral("978"))
309 && !input_.startsWith(QStringLiteral("979"))) {
310 if(input_.length() == 9) input_.resize(10);
311 input_[9] = checkSum10(input_);
312 }
313
314 ulong range = input_.leftJustified(9, QLatin1Char('0'), true).toULong();
315
316 // now find which band the range falls in
317 int band = 0;
318 while(range >= bands[band].MaxValue) {
319 ++band;
320 }
321
322 // if we have space to put the first hyphen, do it
323 if(input_.length() > bands[band].First) {
324 input_.insert(bands[band].First, QLatin1Char('-'));
325 }
326
327 //add 1 since one "-" character has already been inserted
328 if(bands[band].Mid != 0) {
329 hyphen2_position = bands[band].Mid;
330 if(static_cast<int>(input_.length()) > (hyphen2_position + 1)) {
331 input_.insert(hyphen2_position + 1, QLatin1Char('-'));
332 }
333 // or put back user's hyphen
334 } else if(hyphen2_position > 0 && static_cast<int>(input_.length()) >= (hyphen2_position + 1)) {
335 input_.insert(hyphen2_position + 1, QLatin1Char('-'));
336 }
337
338 // add a "-" before the checkdigit and another one if the middle "-" exists
339 const int trueLast = bands[band].Last + 1 + (hyphen2_position > 0 ? 1 : 0);
340 if(input_.length() > trueLast) {
341 input_.insert(trueLast, QLatin1Char('-'));
342 } else if(hyphenAtEnd && !input_.endsWith(QLatin1Char('-'))) {
343 input_ += QLatin1Char('-');
344 }
345 }
346
fixup13(QString & input_)347 void ISBNValidator::fixup13(QString& input_) {
348 if(input_.isEmpty()) {
349 return;
350 }
351
352 // remove invalid chars
353 static const QRegularExpression badChars(QStringLiteral("[^\\d-]"));
354 input_.remove(badChars);
355
356 // hyphen placement for some languages publishers is well-defined
357 // remove all hyphens, and insert them ourselves
358 // some countries have ill-defined second hyphen positions, and if
359 // the user inserts one, then be sure to put it back
360
361 QString after = input_.mid(3);
362 if(after[0] == QLatin1Char('-')) {
363 after = after.mid(1);
364 }
365
366 // Find the first hyphen. If there is none,
367 // input_.indexOf('-') returns -1 and hyphen1_position = -1
368 int hyphen1_position = after.indexOf(QLatin1Char('-'));
369
370 // Find the second one. If none, hyphen2_position = -2
371 int hyphen2_position = after.indexOf(QLatin1Char('-'), hyphen1_position+1) - 1;
372
373 // The second hyphen can not be in the last characters
374 if(hyphen2_position >= 9) {
375 hyphen2_position = 0;
376 }
377
378 // Remove all existing hyphens. We will insert ours.
379 after.remove(QLatin1Char('-'));
380 after.truncate(10);
381
382 // add the checksum
383 if(after.length() > 8) {
384 if(after.length() == 9) after.resize(10);
385 after[9] = checkSum13(input_.left(3) + after);
386 }
387
388 ulong range = after.leftJustified(9, QLatin1Char('0'), true).toULong();
389
390 // now find which band the range falls in
391 int band = 0;
392 while(range >= bands[band].MaxValue) {
393 ++band;
394 }
395
396 // if we have space to put the first hyphen, do it
397 if(after.length() > bands[band].First) {
398 after.insert(bands[band].First, QLatin1Char('-'));
399 }
400
401 //add 1 since one "-" has already been inserted
402 if(bands[band].Mid != 0) {
403 hyphen2_position = bands[band].Mid;
404 if(static_cast<int>(after.length()) > (hyphen2_position + 1)) {
405 after.insert(hyphen2_position + 1, QLatin1Char('-'));
406 }
407 // or put back user's hyphen
408 } else if(hyphen2_position > 0 && static_cast<int>(after.length()) >= (hyphen2_position + 1)) {
409 after.insert(hyphen2_position + 1, QLatin1Char('-'));
410 }
411
412 // add a "-" before the checkdigit and another one if the middle "-" exists
413 int trueLast = bands[band].Last + 1 + (hyphen2_position > 0 ? 1 : 0);
414 if(after.length() > trueLast) {
415 after.insert(trueLast, QLatin1Char('-'));
416 }
417 input_ = input_.left(3) + QLatin1Char('-') + after;
418 }
419
checkSum10(const QString & input_)420 QChar ISBNValidator::checkSum10(const QString& input_) {
421 uint sum = 0;
422 uint multiplier = 10;
423
424 // hyphens are already gone, only use first nine digits
425 for(int i = 0; i < input_.length() && multiplier > 1; ++i) {
426 sum += input_[i].digitValue() * multiplier--;
427 }
428 sum %= 11;
429 sum = 11-sum;
430
431 QChar c;
432 if(sum == 10) {
433 c = QLatin1Char('X');
434 } else if(sum == 11) {
435 c = QLatin1Char('0');
436 } else {
437 c = QString::number(sum)[0];
438 }
439 return c;
440 }
441
checkSum13(const QString & input_)442 QChar ISBNValidator::checkSum13(const QString& input_) {
443 uint sum = 0;
444
445 const int len = qMin(12, input_.length());
446 // hyphens are already gone, only use first twelve digits
447 for(int i = 0; i < len; ++i) {
448 sum += input_[i].digitValue() * (1 + 2*(i%2));
449 // multiplier goes 1, 3, 1, 3, etc...
450 }
451 sum %= 10;
452 sum = 10-sum;
453
454 QChar c;
455 if(sum == 10) {
456 c = QLatin1Char('0');
457 } else {
458 c = QString::number(sum)[0];
459 }
460 return c;
461 }
462
463 // ISBN code from Regis Boudin
464 #define ISBNGRP_1DIGIT(digit, max, middle, last) \
465 {((digit)*100000000) + (max), 1, middle, last}
466 #define ISBNGRP_2DIGIT(digit, max, middle, last) \
467 {((digit)*10000000) + ((max)/10), 2, middle, last}
468 #define ISBNGRP_3DIGIT(digit, max, middle, last) \
469 {((digit)*1000000) + ((max)/100), 3, middle, last}
470 #define ISBNGRP_4DIGIT(digit, max, middle, last) \
471 {((digit)*100000) + ((max)/1000), 4, middle, last}
472 #define ISBNGRP_5DIGIT(digit, max, middle, last) \
473 {((digit)*10000) + ((max)/10000), 5, middle, last}
474
475 #define ISBNPUB_2DIGIT(grp) (((grp)+1)*1000000)
476 #define ISBNPUB_3DIGIT(grp) (((grp)+1)*100000)
477 #define ISBNPUB_4DIGIT(grp) (((grp)+1)*10000)
478 #define ISBNPUB_5DIGIT(grp) (((grp)+1)*1000)
479 #define ISBNPUB_6DIGIT(grp) (((grp)+1)*100)
480 #define ISBNPUB_7DIGIT(grp) (((grp)+1)*10)
481 #define ISBNPUB_8DIGIT(grp) (((grp)+1)*1)
482
483 // how to format an ISBN, after categorising it into a range of numbers.
484 struct ISBNValidator::isbn_band ISBNValidator::bands[] = {
485 /* Groups 0 & 1 : English */
486 ISBNGRP_1DIGIT(0, ISBNPUB_2DIGIT(19), 3, 9),
487 ISBNGRP_1DIGIT(0, ISBNPUB_3DIGIT(699), 4, 9),
488 ISBNGRP_1DIGIT(0, ISBNPUB_4DIGIT(8499), 5, 9),
489 ISBNGRP_1DIGIT(0, ISBNPUB_5DIGIT(89999), 6, 9),
490 ISBNGRP_1DIGIT(0, ISBNPUB_6DIGIT(949999), 7, 9),
491 ISBNGRP_1DIGIT(0, ISBNPUB_7DIGIT(9999999), 8, 9),
492
493 ISBNGRP_1DIGIT(1, ISBNPUB_5DIGIT(54999), 6, 9),
494 ISBNGRP_1DIGIT(1, ISBNPUB_5DIGIT(86979), 6, 9),
495 ISBNGRP_1DIGIT(1, ISBNPUB_6DIGIT(998999), 7, 9),
496 ISBNGRP_1DIGIT(1, ISBNPUB_7DIGIT(9999999), 8, 9),
497 /* Group 2 : French */
498 ISBNGRP_1DIGIT(2, ISBNPUB_2DIGIT(19), 3, 9),
499 ISBNGRP_1DIGIT(2, ISBNPUB_3DIGIT(349), 4, 9),
500 ISBNGRP_1DIGIT(2, ISBNPUB_5DIGIT(39999), 6, 9),
501 ISBNGRP_1DIGIT(2, ISBNPUB_3DIGIT(699), 4, 9),
502 ISBNGRP_1DIGIT(2, ISBNPUB_4DIGIT(8399), 5, 9),
503 ISBNGRP_1DIGIT(2, ISBNPUB_5DIGIT(89999), 6, 9),
504 ISBNGRP_1DIGIT(2, ISBNPUB_6DIGIT(949999), 7, 9),
505 ISBNGRP_1DIGIT(2, ISBNPUB_7DIGIT(9999999), 8, 9),
506
507 /* Group 2 : German */
508 ISBNGRP_1DIGIT(3, ISBNPUB_2DIGIT(19), 3, 9),
509 ISBNGRP_1DIGIT(3, ISBNPUB_3DIGIT(699), 4, 9),
510 ISBNGRP_1DIGIT(3, ISBNPUB_4DIGIT(8499), 5, 9),
511 ISBNGRP_1DIGIT(3, ISBNPUB_5DIGIT(89999), 6, 9),
512 ISBNGRP_1DIGIT(3, ISBNPUB_6DIGIT(949999), 7, 9),
513 ISBNGRP_1DIGIT(3, ISBNPUB_7DIGIT(9999999), 8, 9),
514
515 ISBNGRP_1DIGIT(7, ISBNPUB_2DIGIT(99), 0, 9),
516 /* Group 80 : Czech */
517 ISBNGRP_2DIGIT(80, ISBNPUB_2DIGIT(19), 4, 9),
518 ISBNGRP_2DIGIT(80, ISBNPUB_3DIGIT(699), 5, 9),
519 ISBNGRP_2DIGIT(80, ISBNPUB_4DIGIT(8499), 6, 9),
520 ISBNGRP_2DIGIT(80, ISBNPUB_5DIGIT(89999), 7, 9),
521 ISBNGRP_2DIGIT(80, ISBNPUB_6DIGIT(949999), 8, 9),
522
523 /* Group 83 : Poland */
524 ISBNGRP_2DIGIT(83, ISBNPUB_2DIGIT(19), 4, 9),
525 ISBNGRP_2DIGIT(83, ISBNPUB_3DIGIT(599), 5, 9),
526 ISBNGRP_2DIGIT(83, ISBNPUB_5DIGIT(69999), 7, 9),
527 ISBNGRP_2DIGIT(83, ISBNPUB_4DIGIT(8499), 6, 9),
528 ISBNGRP_2DIGIT(83, ISBNPUB_5DIGIT(89999), 7, 9),
529 ISBNGRP_2DIGIT(83, ISBNPUB_6DIGIT(949999), 8, 9),
530
531 /* Group 90 * Netherlands */
532 ISBNGRP_2DIGIT(90, ISBNPUB_2DIGIT(19), 4, 9),
533 ISBNGRP_2DIGIT(90, ISBNPUB_3DIGIT(499), 5, 9),
534 ISBNGRP_2DIGIT(90, ISBNPUB_4DIGIT(6999), 6, 9),
535 ISBNGRP_2DIGIT(90, ISBNPUB_5DIGIT(79999), 7, 9),
536 ISBNGRP_2DIGIT(90, ISBNPUB_6DIGIT(849999), 8, 9),
537 ISBNGRP_2DIGIT(90, ISBNPUB_4DIGIT(8999), 6, 9),
538 ISBNGRP_2DIGIT(90, ISBNPUB_7DIGIT(9999999), 9, 9),
539
540 ISBNGRP_2DIGIT(94, ISBNPUB_2DIGIT(99), 0, 9),
541 ISBNGRP_3DIGIT(993, ISBNPUB_2DIGIT(99), 0, 9),
542 ISBNGRP_4DIGIT(9989, ISBNPUB_2DIGIT(99), 0, 9),
543 ISBNGRP_5DIGIT(99999, ISBNPUB_2DIGIT(99), 0, 9)
544 };
545
operator ()(const QString & value1_,const QString & value2_) const546 bool Tellico::ISBNComparison::operator()(const QString& value1_, const QString& value2_) const {
547 QString value1 = ISBNValidator::cleanValue(value1_).toUpper();
548 QString value2 = ISBNValidator::cleanValue(value2_).toUpper();
549
550 if(value1 == value2) {
551 return true;
552 }
553 const int len1 = value1.length();
554 const int len2 = value2.length();
555 if(len1 < 10 || len2 < 10) {
556 // they're not ISBN values at all
557 return false;
558 }
559 if(len1 == 13) {
560 ISBNValidator::fixup13(value1);
561 } else {
562 value1 = ISBNValidator::isbn13(value1);
563 }
564 if(len2 == 13) {
565 ISBNValidator::fixup13(value2);
566 } else {
567 value2 = ISBNValidator::isbn13(value2);
568 }
569 return value1 == value2;
570 }
571