/************************************************************************ MeOS - Orienteering Software Copyright (C) 2009-2024 Melin Software HB 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 3 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, see . Melin Software HB - software@melin.nu - www.melin.nu Eksoppsvägen 16, SE-75646 UPPSALA, Sweden ************************************************************************/ #include "stdafx.h" #include "oFreeImport.h" #include "oEvent.h" #include #include "gdioutput.h" #include "gdifonts.h" #include "localizer.h" #include "meosexception.h" #include "meos_util.h" #include "io.h" #include "fcntl.h" #include "sys/stat.h" #include #include void oWordDB::insert(const wchar_t *s) { str.insert(s); } bool oWordDB::lookup(const wchar_t *s) const { return str.count(s)==1; } const char *oWordDB::deserialize(const char *bf, const char *end) { BYTE s=bf[0]; bf++; str.clear(); for (int k=0;kend) throw std::exception("Internal error deserializing wordlist."); str.insert(ns); } return bf; } char *oWordDB::serialize(char *bf) const { //Randomize order for better search performace on reload. BYTE s=BYTE(str.size()); vector rnd(s); int k=0; for (set::const_iterator it=str.begin(); it!=str.end(); ++it) rnd[k++]=*it; bf[0]=s; bf++; for (k=0;k::const_iterator it=str.begin(); it!=str.end(); ++it) s+=(it->size()+1)*sizeof(wchar_t); return s; } pWordDatabase oWordDB::split() { if (str.size()>=hashSplitSize) { oWordIndexHash *db=new oWordIndexHash(false); set::iterator it; for(it=str.begin();it!=str.end();++it) db->insert(it->c_str()); delete this; return db; } else return this; } size_t oWordDB::size() { return str.size(); } void oWordIndexHash::clear() { MapTable::iterator it; for(it=unMapped.begin(); it!=unMapped.end(); ++it) if (it->second) delete it->second; unMapped.clear(); for(int k=0;kdeserialize(bf, end); } DWORD i=a-indexMapStart; if (i<=(indexMapEnd-indexMapStart)) { if (hashTable[i]) throw std::exception("Internal error deserilizing wordlist."); else hashTable[i]=db; } else unMapped[a]=db; } return bf; } char *oWordIndexHash::serialize(char *bf) const { unsigned short s = WORD(unMapped.size()); for (int k=0;kfirst; if (it->first) { *((short *)&bf[2]) = it->second->getType(); bf = it->second->serialize(bf+4); } else { //Empty string. *((short *)&bf[2]) = 0; bf+=4; } } for (int k=0;kgetType(); bf = hashTable[k]->serialize(bf+4); } } return bf; } int oWordIndexHash::serialSize() const { int s=2; for (int k=0;kserialSize()+4; } for(MapTable::const_iterator it=unMapped.begin(); it!=unMapped.end(); ++it) s+=it->second ? it->second->serialSize()+4 : 4; return s; } void oWordIndexHash::insert(const wchar_t *s) { DWORD i=s[0]-indexMapStart; if (i<=(indexMapEnd-indexMapStart)) { if (!hashTable[i]) { hashTable[i]=new oWordDB(); hashTable[i]->insert(s+1); } else { hashTable[i]=hashTable[i]->split(); hashTable[i]->insert(s+1); } } else { if (s[0]==0) unMapped[0]=0; //Empty string else { MapTable::iterator it=unMapped.find(s[0]); if (it==unMapped.end()) { pWordDatabase db(hashAll ? pWordDatabase(new oWordIndexHash(false)): pWordDatabase(new oWordDB())); db->insert(s+1); unMapped[s[0]]=db; } else { if (it->second) { it->second=it->second->split(); it->second->insert(s+1); } else assert(s[0]==0); } } } } bool oWordIndexHash::lookup(const wchar_t *s) const { DWORD i = s[0] - indexMapStart; if (i<=(indexMapEnd-indexMapStart)) { if (!hashTable[i]) return false; else return hashTable[i]->lookup(s+1); } else { MapTable::const_iterator it=unMapped.find(s[0]); if (it==unMapped.end()) return false; else if (s[0]==0) return true; else return it->second->lookup(s+1); } } oWordIndexHash::oWordIndexHash(bool hashAll_) : hashAll(hashAll_) { for(int k=0;k &serial) const { int s=wh.serialSize(); serial.resize(s); wh.serialize(&serial[0]); } void oWordList::deserialize(const vector &serial) { wh.clear(); wh.deserialize(&serial[0], &serial[0]+serial.size()); } void oWordList::save(const wstring &file) const { int f=-1; _wsopen_s(&f, file.c_str(), _O_BINARY|_O_CREAT|_O_TRUNC|_O_WRONLY, _SH_DENYWR, _S_IREAD|_S_IWRITE); if (f!=-1) { vector serial; serialize(serial); const char *hdr="WWDB"; _write(f, hdr, 4); DWORD s=serial.size(); _write(f, &s, 4); _write(f, &serial[0], s); _close(f); } else throw std::exception("Could not save word database."); } void oWordList::load(const wstring &file) { wstring ex = L"Bad word database. " +file; int f=-1; _wsopen_s(&f, file.c_str(), _O_BINARY|_O_RDONLY, _SH_DENYWR, _S_IREAD|_S_IWRITE); if (f!=-1) { char hdr[5]={0,0,0,0,0}; _read(f, hdr, 4); if ( strcmp(hdr, "WWDB")!=0 ) throw meosException(ex); DWORD s=0; _read(f, &s, 4); vector serial(s); if (_read(f, &serial[0], s)!=s) throw meosException(ex); _close(f); deserialize(serial); } else throw meosException(ex); } oFreeImport::oFreeImport(void) { for(int k=1;k<30;k++) separator.insert(k); separator.insert(' '); separator.insert(','); separator.insert(';'); separator.insert('('); separator.insert(')'); separator.insert('/'); loaded=false; } oFreeImport::~oFreeImport(void) { } wchar_t *trim(wchar_t *str) { int k=wcslen(str)-1; while(k>=0 && (iswspace(str[k]) || str[k] == 160)) str[k--] = 0; while(iswspace(*str) || str[k]==160) str++; return str; } wchar_t *oFreeImport::extractPart(wchar_t *&str, int &wordCount) const { wordCount=0; while (separator.count(*str)) str++; if (!*str) return str; wchar_t *out=str; wordCount=1; while (*str && (!separator.count(*str) || *str==' ')) { if (*str==' ') { while (*++str==' '); if (separator.count(*str) || !*str) { if (*str) *str++=0; return trim(out); } wordCount++; } str++; } if (*str) *str++=0; return trim(out); } wchar_t *oFreeImport::extractWord(wchar_t *&str, int &count) const { count=0; while (separator.count(*str)) str++; if (!*str) return str; wchar_t *out=str; count=1; while (*str && !separator.count(*str)) { count++; str++; } if (*str) *str++=0; return out; } wchar_t *oFreeImport::extractLine(wchar_t *&str, int &count) const { count=0; while (*str=='\n') str++; wchar_t *out=str; while (*str && *str!='\n') { str++; count++; } if (*str) *str++=0; return out; } oEntryPerson::oEntryPerson(const wstring &clb) { club = clb; cardNo=0; } void oEntryPerson::swap() { if (!name1.empty()) std::swap(name1, name2); } int oEntryPerson::nameCount() const { if (!name2.empty()) return countWords(name1.c_str()) + countWords(name2.c_str()); else return countWords(name1.c_str()); } void oEntryBlock::clear(const wstring &rulingClub, const wstring &rulingClass) { ePersons.clear(); eClub = rulingClub; eClass = rulingClass; eStartTime.clear(); canInsertName = true; isClassSet = false; // Count only explicit set class nClubsSet = rulingClub.empty() ? 0 : 1; } oEntryBlock::oEntryBlock(const oFreeImport &importer) : freeImporter(importer) { canInsertName=true; nClubsSet = 0; isClassSet = false; } oEntryBlock::oEntryBlock(const oEntryBlock &eb) : freeImporter(eb.freeImporter) { ePersons = eb.ePersons; eClub = eb.eClub; eClass = eb.eClass; eStartTime = eb.eStartTime; canInsertName = eb.canInsertName; isClassSet = eb.isClassSet; nClubsSet = eb.nClubsSet; } void oEntryBlock::operator=(const oEntryBlock &eb) { ePersons = eb.ePersons; eClub = eb.eClub; eClass = eb.eClass; eStartTime = eb.eStartTime; canInsertName = eb.canInsertName; isClassSet = eb.isClassSet; nClubsSet = eb.nClubsSet; } void oEntryBlock::setClass(const wchar_t *s) { eClass=s; if (!ePersons.empty()) canInsertName=false; isClassSet = true; completeName(); } void oEntryBlock::setClub(const wchar_t *s) { eClub = s; nClubsSet++; for (size_t k = 0; k=freeImporter.getExpectedNumRunners(eClass)) canInsertName=false; completeName(); } void oEntryBlock::completeName() { if (!ePersons.empty() && !ePersons.back().name1.empty() && ePersons.back().name2.empty()) ePersons.back().name2=L"*"; } void oEntryBlock::setStartTime(const wchar_t *s) { eStartTime=s; if (!ePersons.empty()) canInsertName=false; completeName(); } void oEntryBlock::setCardNo(int c) { if (ePersons.empty() || ePersons.back().cardNo!=0) ePersons.push_back(oEntryPerson(eClub)); for (size_t i = 0; i < ePersons.size(); i++) { if (ePersons[i].cardNo == 0) { ePersons[i].cardNo=c; break; } } completeName(); } bool oEntryBlock::needCard() const { if (ePersons.empty()) return true; for (size_t i = 0; i < ePersons.size(); i++) { if (ePersons[i].cardNo == 0) { return true; } } return false; } wstring oEntryBlock::getTeamName() const { set clubs; if (!eClub.empty()) clubs.insert(eClub); for (size_t k = 0; k 1) { wstring tname; for (set::iterator it = clubs.begin(); it != clubs.end(); ++it) { if (!tname.empty()) tname += L"/"; tname += *it; } return tname; } else return lang.tl("Lag"); } wstring oEntryBlock::getName(int k) const { if (size_t(k)>=ePersons.size()) return lang.tl("N.N."); wstring n=ePersons[k].name1; if (!ePersons[k].name2.empty()) n+=L" "+ePersons[k].name2; return n; } wstring oEntryBlock::getClub(int k) const { if (size_t(k)>=ePersons.size()) return eClub; const wstring &c=ePersons[k].club; if (!c.empty()) return c; return eClub; } int oEntryBlock::getCard(int k) const { if (k oEntryBlock::getPersons() const { vector names; wstring n; wchar_t bf[256]; for (size_t k=0;k0) { swprintf_s(bf, L" (%d)", ePersons[k].cardNo); n+=bf; } names.push_back(n); n.clear(); } return names; } int oEntryBlock::nameCount() { if (ePersons.empty()) return 0; int space=0; const wstring &name1=ePersons.back().name1; const wstring &name2=ePersons.back().name2; if (!name1.empty()) { space++; for (size_t k=0;k=2 && countWords(s)>=2) completeName(); if (complete) { if (!ePersons.back().name1.empty()) ePersons.push_back(oEntryPerson(eClub)); ePersons.back().name1=s; } else { if (ePersons.back().name1.empty()) ePersons.back().name1=s; else if (ePersons.back().name2.empty()) ePersons.back().name2=s; else { ePersons.push_back(oEntryPerson(eClub)); ePersons.back().name1=s; } } } bool oEntryBlock::acceptMoreClubs(int expectedNumRunners) const { return nClubsSet < int(ePersons.size()) || nClubsSet < expectedNumRunners; } bool oEntryBlock::expectMoreNames(int expectedNumRunners) const { return int(ePersons.size()) < expectedNumRunners || (expectedNumRunners == ePersons.size() && ePersons.back().nameCount()<=1); } void oEntryBlock::cleanEntry() { for (size_t k=0;k &b, int &offset, int &delta) const { vector d; d.reserve(b.size()/3); for (size_t k=0;k1) { int x=offset; while(unsigned(x)=1000 && k<10000000); } bool oFreeImport::isTime(const wstring &m) const { if (m.empty() || m.length()>9) return false; for(size_t k=0;k23) return false; int minute=0; int second=0; int kp=m.find_first_of(':'); if (kp>0) { wstring mtext=m.substr(kp+1); minute=_wtoi(mtext.c_str()); if (minute<0 || minute>60) return false; kp=mtext.find_last_of(':'); if (kp>0) { second=_wtoi(mtext.substr(kp+1).c_str()); if (second<0 || second>60) return false; } } int t = hour * timeConstHour + minute * timeConstMinute + second * timeConstSecond; if (t<=0) return false; return true; } bool oFreeImport::isCrap(const wchar_t *p) const { int s=wcslen(p); for (int k=0;k='0' && p[k]<='9')) return true; } return false; } bool oFreeImport::isName(const wchar_t *p) const { wchar_t bf[1024]; wcscpy_s(bf, p); wchar_t *str=bf; int c=1; vector w; while(c>0) { const wchar_t *out= extractWord(str,c); if (c>0) w.push_back(out); } if (w.size()==1) return familyDB.lookup(w[0]) || givenDB.lookup(w[0]); else if (w.size()<4) { int n=0; int t=(w.size()+1)/2; for (size_t k=0;k=t; } return false; } void oFreeImport::analyzePart(wchar_t *part, const MatchPattern &ptrn, int nNamesPerPart, oEntryBlock &entry, vector &entries, bool allowName) { vector words; int count=1; while (count>0) { const wchar_t *out=extractWord(part, count); if (count>0) words.push_back(out); } allowName &= !entry.hasName() || nNamesPerPart!=1; vector isNameV(words.size(), false); vector isClub(words.size(), false); vector isClass(words.size(), false); vector isCardV(words.size(), false); vector isTimeV(words.size(), false); vector used(words.size(), false); int names=0; int classes=0; int clubs=0; int cards=0; int times=0; for (size_t j=0;j0 && (clubs+classes+cards+times)<1) { if (!entry.canInsertName) { entry.cleanEntry(); entries.push_back(entry); entry.clear(rulingClub, rulingClass); } wstring n; for (size_t j=0;j0 && !entry.expectMoreNames(getExpectedNumRunners(entry.eClass)) && !allowName) lastType = Club; //Guess club for (size_t k=0;k2); lastInsertedType = Name; } } void oFreeImport::test(const oRunnerList &li) { oRunnerList::const_iterator it; //classDB.insert("H21"); //classDB.insert("Herrar"); for(it=li.begin();it!=li.end();++it) { givenDB.insert(it->getGivenName().c_str()); familyDB.insert(it->getFamilyName().c_str()); clubDB.insert(it->getClub().c_str()); classDB.insert(it->getClass(true).c_str()); } wstring b(L"0"), c(L"a2"), d(L"a"); const int end='z'+5; for(int k='a';k<=end+1;k++) if (k>end) givenDB.insert(b.c_str()); else { wstring z=b+wchar_t(k); for( int j='a';j<=end+1;j++) { if (j>end) givenDB.insert(z.c_str()); else { givenDB.insert( (z+wchar_t(j)+d).c_str() ); } } } givenDB.save(L"test.mwd"); oWordList wl; wl.load(L"test.mwd"); wl.save(L"test2.mwd"); for(it=li.begin();it!=li.end();++it) givenDB.insert(it->getGivenName().c_str()); wl.save(L"test3.mwd"); } /** Must not return 0 */ int oFreeImport::getExpectedNumRunners(const wstring &cls) const { wstring key(canonizeName(cls.c_str())); map::const_iterator it = runnersPerClass.find(key); if (it != runnersPerClass.end()) return it->second; else return 1; } void oFreeImport::init(const oRunnerList &r, const oClubList &clb, const oClassList &cls) { runnersPerClass.clear(); vector split_vector; wstring sep(L" "); for(oRunnerList::const_iterator it=r.begin();it!=r.end();++it) { givenDB.insert(it->getGivenName().c_str()); wstring f=it->getFamilyName(); familyDB.insert(f.c_str()); split(f, sep, split_vector); if (split_vector.size()>1) for(size_t k=0;kgetName().c_str()); split(it->getName().c_str(), sep, split_vector); if (split_vector.size()>1) for(size_t k=0;kgetName().begin(), it->getName().end()); classDB.insert(nname.c_str()); split(nname.c_str(), sep, split_vector); if (split_vector.size()>1) for(size_t k=0;kgetNumDistinctRunners(); wstring key(canonizeName(nname.c_str())); runnersPerClass[key] = numrunner; } const wchar_t *clsPrefix[8] = {L"h", L"d", L"m", L"w", L"herrar", L"damer", L"men", L"women"}; wchar_t bf[64]; vector ages; for (int k=10;k<=16;k++) ages.push_back(k); ages.push_back(18); ages.push_back(20); ages.push_back(21); for (int k=35;k<=100;k+=5) ages.push_back(k); for (int k=0;k<8;k++) { classDB.insert(clsPrefix[k]); for (size_t j=0; j=18 && ages[j]<=21) { swprintf_s(bf, L"%s %dE", clsPrefix[k], ages[j]); classDB.insert(bf); swprintf_s(bf, L"%s %d Elit", clsPrefix[k], ages[j]); classDB.insert(bf); swprintf_s(bf, L"%s%dE", clsPrefix[k], ages[j]); classDB.insert(bf); swprintf_s(bf, L"%s%d Elit", clsPrefix[k], ages[j]); classDB.insert(bf); } } } classDB.insert(L"L"); classDB.insert(L"Lång"); classDB.insert(L"E"); classDB.insert(L"Elit"); classDB.insert(L"Elite"); classDB.insert(L"Adult"); classDB.insert(L"Vuxen"); classDB.insert(L"Insk"); classDB.insert(L"Insk."); classDB.insert(L"Inskolning"); classDB.insert(L"M"); classDB.insert(L"ÖM"); classDB.insert(L"Motion"); classDB.insert(L"K"); classDB.insert(L"Kort"); classDB.insert(L"Mellan"); classDB.insert(L"Svår"); classDB.insert(L"Lätt"); classDB.insert(L"Kortlätt"); classDB.insert(L"Långsvår"); classDB.insert(L"km"); classDB.insert(L"Short"); classDB.insert(L"Long"); classDB.insert(L"Öppen"); classDB.insert(L"Ö"); classDB.insert(L"Ungdom"); classDB.insert(L"Ung."); classDB.insert(L"U"); classDB.insert(L"U1"); classDB.insert(L"U2"); classDB.insert(L"U3"); classDB.insert(L"U4"); for (int j=1;j<10;j++) { swprintf_s(bf, L"Ö%d", j); classDB.insert(bf); swprintf_s(bf, L"Ö %d", j); classDB.insert(bf); swprintf_s(bf, L"Öppen %d", j); classDB.insert(bf); swprintf_s(bf, L"D%d", j); classDB.insert(bf); swprintf_s(bf, L"Direkt %d", j); classDB.insert(bf); } } bool oFreeImport::isHeaderWord(const wstring &word) const { if (headerWords.empty()) { headerWords.insert(canonizeName(L"namn")); headerWords.insert(canonizeName(L"klass")); headerWords.insert(canonizeName(L"ålder")); headerWords.insert(canonizeName(L"anmälda")); headerWords.insert(canonizeName(L"anmälningar")); headerWords.insert(canonizeName(L"deltagare")); headerWords.insert(canonizeName(L"löpare")); headerWords.insert(canonizeName(L"bricka")); headerWords.insert(canonizeName(L"starttid")); headerWords.insert(canonizeName(L"nummer")); headerWords.insert(canonizeName(L"startnummer")); headerWords.insert(canonizeName(L"bricknummer")); headerWords.insert(canonizeName(L"nummer")); headerWords.insert(canonizeName(L"klubb")); headerWords.insert(canonizeName(L"klassnamn")); headerWords.insert(canonizeName(L"pinne")); headerWords.insert(canonizeName(L"nummerlapp")); headerWords.insert(canonizeName(L"SI")); headerWords.insert(canonizeName(L"förening")); headerWords.insert(canonizeName(L"rank")); headerWords.insert(canonizeName(L"ranking")); headerWords.insert(canonizeName(L"nr-lapp")); headerWords.insert(canonizeName(L"tid")); headerWords.insert(canonizeName(L"name")); headerWords.insert(canonizeName(L"age")); headerWords.insert(canonizeName(L"class")); headerWords.insert(canonizeName(L"start")); headerWords.insert(canonizeName(L"time")); headerWords.insert(canonizeName(L"club")); headerWords.insert(canonizeName(L"card")); } return headerWords.count(canonizeName(word.c_str()))>0; } bool match_char(wchar_t c, const wstring &sep) { for (size_t j = 0; j &line) const { if (line.empty()) return true; int header = 0; int nonheader = 0; bool forceNoHeader = false; vector firstWord; const wstring separators = L" :,.="; for (size_t k = 0; k < line.size(); k++) { vector words; split(line[k], separators, words); if (words.empty()) firstWord.push_back(L""); else firstWord.push_back(words.front()); for (size_t j = 0; j < words.size(); j++) { if (words[j].empty()) continue; if (isHeaderWord(words[j]) ) header++; else { nonheader++; // If the header contains a real name, class or club, do not reject if (isName(words[j].c_str())) forceNoHeader = true; if (classDB.lookup(words[j].c_str())) forceNoHeader = true; if (clubDB.lookup(words[j].c_str()) || clubDB.lookup(line[k])) forceNoHeader = true; } } } if (!forceNoHeader && header > nonheader) return true; // Remove header words for (size_t k = line.size()-1; k < line.size(); k--) { if (isHeaderWord(line[k])) { line.erase(line.begin()+k); } else if (isHeaderWord(firstWord[k])) { line[k] += firstWord[k].size(); int j = 0; while (line[k][j] && match_char(line[k][j], separators)) j++; line[k] += j; } } return false; } const int CLEARROW = 1; const int IGNOREROW = 2; const int COMPLETEROW = 4; int oFreeImport::preAnalyzeRow(vector &p, const vector &ptrn, vector &classified) { int stat = 0; int names = 0; int clubs = 0; int cards = 0; int classes = 0; int unknown = 0; for (size_t j=0;j1) { if (ptrn[j].isClub()) type=0; else if (ptrn[j].isClass()) type=1; else if (ptrn[j].isCard() && _wtoi(p[j])>0) type=3; } classified.push_back(type); if (type == 1) classes++; else if (type==3) cards++; else if (type==4) names++; else if (type==0) clubs++; else unknown++; const wchar_t *canP = canonizeName(p[j]); if (wcscmp(canP, L"vakant")==0 || wcscmp(canP, L"vacant")==0) stat|=IGNOREROW; } if (clubs>0 && names>0 && cards>0) stat|=COMPLETEROW; if (p.size()==1 && (classes>0 || clubs>0)) stat|=CLEARROW; while (cards > names && names>0) { int idx = -1; for (size_t k = 0; k=0 ? _wtoi(p[idx]) : -1; if (a==-1) idx = k; else { int b = _wtoi(p[k]); if (b>0 && b=0) { classified[idx] = -2; cards--; unknown++; } } for (int k = p.size()-1; k>=0;k--) { if (classified[k] == -2) { classified.erase(classified.begin()+k); p.erase(p.begin()+k); } } return stat; } void oFreeImport::extractEntries(wchar_t *str, vector &entries) { entries.clear(); vector< vector > parts; parts.reserve(wcslen(str)/24); set lineTypes; int count=1; while(count>0) { wchar_t *line=extractLine(str, count); if (count>0) { parts.push_back( vector() ); while(count>0) { wchar_t *out=extractPart(line, count); if (count>0) parts.back().push_back(out); } if ( analyzeHeaders(parts.back()) ) { parts.pop_back(); count = 1; } else { lineTypes.insert(parts.back().size()); count=1; } } } map > patterns; map nNames; //Number of name entries per pattern //Analyze line types for(set::iterator it=lineTypes.begin(); it!=lineTypes.end();++it) { vector mp(*it); int limit=0; for (size_t k=0;k &p=parts[k]; for (int j=0;j<*it; j++) { if (classDB.lookup(p[j])) mp[j].nClass++; if (clubDB.lookup(p[j])) mp[j].nClub++; if (isName(p[j])) { mp[j].nName++; if (countWords(p[j])==1) mp[j].singleName++; } if (isCard(p[j])) mp[j].nCard++; if (isTime(p[j])) mp[j].nTime++; } } } if (limit==1) limit=0; else if (limit<=3) limit=1; else if (limit<=10) limit=2; else limit=limit/5; int nn=0; for (size_t k=0;k=mp.size() || !mp[k+1].isName(limit)) && (k==0 || !mp[k-1].isName(limit)) ) { mp[k].singleName=false; } nNames[*it]=nn; swap(patterns[*it], mp); } entries.reserve(parts.size()); oEntryBlock entry(*this); vector words; rulingClub = L""; rulingClass = L""; lastInsertedType = None; for (size_t k=0;k &p=parts[k]; vector classified; int res = preAnalyzeRow(p, patterns[p.size()], classified); int s = p.size(); vector &ptrn=patterns[s]; if (ptrn.empty()) ptrn.resize(s); assert(ptrn.size() == s); lastInsertedType = None; if (res & CLEARROW) { entry.cleanEntry(); if (entry.hasName()) { entries.push_back(entry); } entry.clear(rulingClub, rulingClass); } else if (res & COMPLETEROW) { entry.cleanEntry(); if (entry.hasName()) { entries.push_back(entry); } entry.clear(rulingClub, rulingClass); } if (res & IGNOREROW) continue; for (int j=0;j1) { if (ptrn[j].isClub()) type = Club; else if (ptrn[j].isClass()) type = Class; else if (ptrn[j].isCard() && _wtoi(p[j])>0) type = Card; } //We have a suggested name, but there should only //be one name and we already got one. if ((type == Name && entry.hasName() && (2*nNames[s])<=readNames)) type = Unknown; if (type==Name && isCrap(p[j])) type = Unknown; if (type == Unknown) analyzePart(p[j], ptrn[j], nNames[s], entry, entries, (2*nNames[s])>readNames); else if (type == Club) { bool moreClubs = entry.acceptMoreClubs(getExpectedNumRunners(entry.eClass)); if (!moreClubs && lastInsertedType == Club) continue; if (!moreClubs && entry.hasName()) { readNames=0; entry.cleanEntry(); entries.push_back(entry); entry.clear(rulingClub, rulingClass); } else if (j<=1 && s<=2) // Set a ruling club if it first on a line containing at most 2 parts. (We allow club+class) rulingClub = p[j]; lastInsertedType = Club; entry.setClub(p[j]); // If we have an odd number of names read // we were expecting a completing name, // but none came, so we complete what we have instead. if (readNames&1) { readNames++; entry.completeName(); } } else if (type == Class) { if (lastInsertedType == Class) continue; if (entry.hasClass() && entry.hasName()) { readNames=0; entry.cleanEntry(); entries.push_back(entry); entry.clear(rulingClub, rulingClass); } else if (j<=1 && s<=2) // Set a ruling class if it first on a line containing at most 2 parts. (We allow club+class) rulingClass = p[j]; lastInsertedType = Class; entry.setClass(p[j]); if (readNames&1) { readNames++; entry.completeName(); } } else if (type == Time) { if (lastInsertedType == Time) continue; if (!entry.eStartTime.empty()) { if (entry.ePersons.size()>1 && !entry.hasName()) entry.ePersons.pop_back(); //Warning if (entry.hasName()) { readNames=0; entry.cleanEntry(); entries.push_back(entry); entry.clear(rulingClub, rulingClass); } } lastInsertedType = Time; entry.setStartTime(p[j]); if (readNames&1) { readNames++; entry.completeName(); } } else if (type == Card) { if (lastInsertedType == Card && !entry.needCard()) continue; lastInsertedType = Card; entry.setCardNo(_wtoi(p[j])); if (readNames&1) { readNames++; entry.completeName(); } } else if (type == Name) { lastInsertedType = Name; bool complete = ptrn[j].isCompleteName(); entry.addPerson(p[j], complete); readNames += complete ? 2:1; } }// End loop over parts if (entry.hasName()) { entry.cleanEntry(); entries.push_back(entry); entry.clear(rulingClub, rulingClass); } else if (res & COMPLETEROW) { entry.cleanEntry(); if (entry.hasName()) entries.push_back(entry); // Else lost data entry.clear(rulingClub, rulingClass); } } for (size_t k=1;k=0; k--) { if (entries[k].eClub.empty()) entries[k].eClub=entries[k+1].eClub; if (entries[k].eClass.empty()) entries[k].eClass=entries[k+1].eClass; } } void oFreeImport::showEntries(gdioutput &gdi, const vector &entries) { gdi.setCX(20); for (size_t k=0;k names = entries[k].getPersons(); for (size_t j=0;j &entries) { map teamno; //For automatic name generation Club/Class for (size_t k=0;kgetClass(entries[k].eClass); if (!pc) { pc=oe->addClass(entries[k].eClass); if (entries[k].getNumPersons()>1) pc->setNumStages(entries[k].getNumPersons()); pc->synchronize(); } if (pc) { int nr=pc->getNumDistinctRunners(); if (nr==1) { pRunner r=oe->addRunner(entries[k].getName(0), entries[k].getClub(0), pc->getId(), entries[k].getCard(0), L"", true); r->setStartTimeS(entries[k].eStartTime); r->setCardNo(entries[k].getCard(0), false); r->addClassDefaultFee(false); r->synchronize(); } else { pClub club=oe->getClubCreate(0, entries[k].eClub); wstring team = entries[k].getTeamName(); //int id=pc->getId() + 10000 * (club ? club->getId() : 0); //char tname[256]; //sprintf_s(tname, "%s %d", entries[k].eClub.c_str(), ++teamno[id]); pTeam t=oe->addTeam(team, club ? club->getId() : 0, pc->getId()); if (t) { t->setStartNo(t->getId(), oBase::ChangeType::Update); for (int j=0;jaddRunner(entries[k].getName(j), entries[k].getClub(j), pc->getId(), entries[k].getCard(j), L"", false); r->setCardNo(entries[k].getCard(j), false); r->addClassDefaultFee(false); t->setRunner(j, r, true); } t->apply(oBase::ChangeType::Update, nullptr); t->synchronize(); } } } } oe->makeUniqueTeamNames(); } void oFreeImport::load() { wchar_t bf[260]; bool warn=false; getUserFile(bf, L"wfamily.mwd"); try { familyDB.load(bf); } catch(std::exception &) {warn=true;} getUserFile(bf, L"wgiven.mwd"); try { givenDB.load(bf); } catch(std::exception &) {warn=true;} getUserFile(bf, L"wclub.mwd"); try { clubDB.load(bf); } catch(std::exception &) {warn=true;} getUserFile(bf, L"wclass.mwd"); try { classDB.load(bf); } catch(std::exception &) {warn=true;} loaded=true; } #include "RunnerDB.h" void oFreeImport::buildDatabases(oEvent &oe) { vector given; vector family; oe.getRunnerDatabase().getAllNames(given, family); for (size_t k = 0; k < given.size(); k++) givenDB.insert(given[k].c_str()); for (size_t k = 0; k < given.size(); k++) familyDB.insert(family[k].c_str()); const vector &clubs = oe.getRunnerDatabase().getClubDB(false); for (size_t k = 0; k< clubs.size(); k++) { clubDB.insert(clubs[k].getName().c_str()); //clubs[k].altNames } //oe.init(*this); save(); } void oEvent::init(oFreeImport &fi) { fi.init(Runners, Clubs, Classes); } void oFreeImport::save() const { wchar_t bf[260]; wstring bu; getUserFile(bf, L"wfamily.mwd"); bu=wstring(bf)+L".old"; _wremove(bu.c_str()); _wrename(bf, bu.c_str()); familyDB.save(bf); getUserFile(bf, L"wgiven.mwd"); bu=wstring(bf)+L".old"; _wremove(bu.c_str()); _wrename(bf, bu.c_str()); givenDB.save(bf); getUserFile(bf, L"wclub.mwd"); bu=wstring(bf)+L".old"; _wremove(bu.c_str()); _wrename(bf, bu.c_str()); clubDB.save(bf); getUserFile(bf, L"wclass.mwd"); bu=wstring(bf)+L".old"; _wremove(bu.c_str()); _wrename(bf, bu.c_str()); classDB.save(bf); }