# SDK Pullenti Lingvo, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved.
# Non-Commercial Freeware and Commercial Software.
# This class is generated using the converter Unisharping (www.unisharping.ru) from Pullenti C# project.
# The latest version of the code is available on the site www.pullenti.ru

import typing
import math
import threading
from pullenti.unisharp.Utils import Utils

from pullenti.morph.MorphNumber import MorphNumber
from pullenti.morph.MorphGender import MorphGender
from pullenti.ner.core.GetTextAttr import GetTextAttr
from pullenti.ner.vacance.internal.VacanceTokenType import VacanceTokenType
from pullenti.ner.core.NounPhraseParseAttr import NounPhraseParseAttr
from pullenti.ner.TextToken import TextToken
from pullenti.ner.org.OrgProfile import OrgProfile
from pullenti.ner.core.TerminParseAttr import TerminParseAttr
from pullenti.ner.NumberToken import NumberToken
from pullenti.ner.core.NounPhraseHelper import NounPhraseHelper
from pullenti.ner.org.OrganizationReferent import OrganizationReferent
from pullenti.ner.person.PersonPropertyReferent import PersonPropertyReferent
from pullenti.ner.Referent import Referent
from pullenti.ner.core.BracketParseAttr import BracketParseAttr
from pullenti.ner.core.BracketHelper import BracketHelper
from pullenti.ner.core.NumberHelper import NumberHelper
from pullenti.ner.ProcessorService import ProcessorService
from pullenti.ner.date.DateRangeReferent import DateRangeReferent
from pullenti.ner.person.internal.PersonAttrToken import PersonAttrToken
from pullenti.ner.MetaToken import MetaToken
from pullenti.ner.person.PersonReferent import PersonReferent
from pullenti.ner.resume.ResumeItemType import ResumeItemType
from pullenti.ner.Analyzer import Analyzer
from pullenti.ner.ReferentToken import ReferentToken
from pullenti.ner.resume.MetaResume import MetaResume
from pullenti.ner.core.MiscHelper import MiscHelper
from pullenti.ner.resume.ResumeItemReferent import ResumeItemReferent
from pullenti.ner.core.internal.ResourceHelper3 import ResourceHelper3
from pullenti.ner.uri.UriReferent import UriReferent
from pullenti.ner.geo.GeoReferent import GeoReferent
from pullenti.ner.date.DateReferent import DateReferent
from pullenti.ner.person.PersonIdentityReferent import PersonIdentityReferent
from pullenti.ner.money.MoneyReferent import MoneyReferent
from pullenti.ner.Token import Token
from pullenti.ner.address.AddressReferent import AddressReferent
from pullenti.ner.phone.PhoneReferent import PhoneReferent
from pullenti.ner.address.StreetReferent import StreetReferent
from pullenti.ner.vacance.internal.VacanceToken import VacanceToken

class ResumeAnalyzer(Analyzer):
    """ Анализатор резюме (специфический анализатор) """
    
    ANALYZER_NAME = "RESUME"
    """ Имя анализатора ("RESUME") """
    
    @property
    def name(self) -> str:
        return ResumeAnalyzer.ANALYZER_NAME
    
    @property
    def caption(self) -> str:
        return "Резюме"
    
    @property
    def description(self) -> str:
        return "Текст содержит одно резюме"
    
    def clone(self) -> 'Analyzer':
        return ResumeAnalyzer()
    
    @property
    def is_specific(self) -> bool:
        """ Специфический анализатор """
        return True
    
    @property
    def type_system(self) -> typing.List['ReferentClass']:
        return [MetaResume.GLOBAL_META]
    
    @property
    def images(self) -> typing.List[tuple]:
        res = dict()
        res[str(MetaResume.IMAGE_ID)] = ResourceHelper3.get_bytes("resume.png")
        return res
    
    def create_referent(self, type0_ : str) -> 'Referent':
        if (type0_ == ResumeItemReferent.OBJ_TYPENAME): 
            return ResumeItemReferent()
        return None
    
    @property
    def progress_weight(self) -> int:
        return 1
    
    def process(self, kit : 'AnalysisKit') -> None:
        ad = kit.get_analyzer_data(self)
        has_sex = False
        has_money = False
        has_pos = False
        has_spec = False
        has_skills = False
        has_exp = False
        has_edu = False
        has_about = False
        rt = None
        pers0 = None
        cou = 0
        t = kit.first_token
        first_pass4424 = True
        while True:
            if first_pass4424: first_pass4424 = False
            else: t = t.next0_
            if (not (t is not None)): break
            tt = MiscHelper.check_image(t)
            if (tt is not None): 
                t = tt
                continue
            pers = Utils.asObjectOrNull(t.get_referent(), PersonReferent)
            if (pers is not None): 
                if (not t.is_newline_before and cou > 10): 
                    break
                if (cou > 10 and t.begin_token.is_value("ИП", None)): 
                    break
                rt = ReferentToken(ad.register_referent(ResumeItemReferent._new3517(ResumeItemType.PERSON, pers)), t, t)
                kit.embed_token(rt)
                pers0 = pers
                break
            if (t.length_char > 1): 
                cou += 1
        opyt = False
        has_edu_org = False
        cur_typ = ResumeItemType.UNDEFINED
        t = kit.first_token
        first_pass4425 = True
        while True:
            if first_pass4425: first_pass4425 = False
            else: t = (None if t is None else t.next0_)
            if (not (t is not None)): break
            if (pers0 is not None): 
                if ((isinstance(t.get_referent(), UriReferent)) or (isinstance(t.get_referent(), PhoneReferent)) or (isinstance(t.get_referent(), AddressReferent))): 
                    uri = Utils.asObjectOrNull(t.get_referent(), UriReferent)
                    if (uri is not None): 
                        if (uri.scheme != "mailto" and uri.scheme != "telegram"): 
                            continue
                        if ("rating@mail.ru" in uri.value): 
                            continue
                    rt = ReferentToken(ad.register_referent(ResumeItemReferent._new3517(ResumeItemType.CONTACT, t.get_referent())), t, t)
                    kit.embed_token(rt)
                    continue
            if (pers0 is not None and not opyt): 
                if ((isinstance(t.get_referent(), GeoReferent)) or (isinstance(t.get_referent(), StreetReferent))): 
                    geo_ = Utils.asObjectOrNull(t.get_referent(), GeoReferent)
                    if (geo_ is not None and not geo_.is_city): 
                        continue
                    rt = ReferentToken(ad.register_referent(ResumeItemReferent._new3517(ResumeItemType.CONTACT, t.get_referent())), t, t)
                    kit.embed_token(rt)
                    continue
            if (t.is_value("КОНТАКТЫ", None)): 
                pass
            if (t.is_value2("ДАТА", "РОЖДЕНИЯ") or t.is_value("РОДИТЬСЯ", None)): 
                tt = t.next0_
                first_pass4426 = True
                while True:
                    if first_pass4426: first_pass4426 = False
                    else: tt = tt.next0_
                    if (not (tt is not None)): break
                    r = tt.get_referent()
                    if (isinstance(r, DateReferent)): 
                        if (pers0 is not None): 
                            pers0.add_slot(PersonReferent.ATTR_BORN, r, False, 0)
                        else: 
                            pass
                        break
                    if (tt.is_value("РОЖДЕНИЕ", None)): 
                        continue
                    if (isinstance(tt.get_referent(), GeoReferent)): 
                        continue
                    if (tt.length_char > 1): 
                        break
                continue
            if (isinstance(t.get_referent(), PersonIdentityReferent)): 
                rt = ReferentToken(ad.register_referent(ResumeItemReferent._new3517(ResumeItemType.DOCUMENT, t.get_referent())), t, t)
                kit.embed_token(rt)
                t = (rt)
                continue
            if (not t.is_newline_before): 
                continue
            if (t.is_table_control_char and t.next0_ is not None): 
                t = t.next0_
            if (t.is_value("ОБРАЗОВАНИЕ", None)): 
                cur_typ = ResumeItemType.STUDY
            if (t.is_value2("ОПЫТ", "РАБОТЫ") or t.is_value2("МЕСТО", "РАБОТЫ") or t.is_value2("ПЕРИОД", "РАБОТЫ")): 
                opyt = True
                cur_typ = ResumeItemType.ORGANIZATION
            rt = ResumeAnalyzer._parse_org(t, cur_typ)
            if (rt is not None): 
                rt.referent = ad.register_referent(rt.referent)
                rt.referent.tag = (rt)
                kit.embed_token(rt)
                cur_typ = rt.referent.typ
                if (cur_typ == ResumeItemType.STUDY): 
                    has_edu_org = True
                t = (rt)
                continue
            rt = ResumeAnalyzer._parse_org2(t, ad, None)
            if (rt is not None): 
                t = (rt)
                cur_typ = ResumeItemType.ORGANIZATION
                continue
            if (not has_sex): 
                rt = ResumeAnalyzer.__parse_sex(t, ad)
                if (rt is not None): 
                    has_sex = True
                    t = (rt)
                    continue
            if (ResumeAnalyzer.__check_geo(t)): 
                continue
            if (not has_money and (isinstance(t.get_referent(), MoneyReferent))): 
                money_ = ResumeItemReferent()
                has_money = True
                money_.typ = ResumeItemType.MONEY
                money_.ref = t.get_referent()
                rt = ReferentToken(ad.register_referent(money_), t, t)
                kit.embed_token(rt)
                t = (rt)
                continue
            if (not has_exp): 
                rt = ResumeAnalyzer.__parse_experience(t, ad)
                if (rt is not None): 
                    has_exp = True
                    t = (rt)
                    continue
            if (not has_spec and t.is_value("СПЕЦИАЛИЗАЦИЯ", None)): 
                if (t.next0_ is not None and t.next0_.is_char(':')): 
                    t = t.next0_
                rt = ResumeAnalyzer.__parse_list(t.next0_, ad, ResumeItemType.SPECIALITY)
                if (rt is not None): 
                    has_spec = True
                    t = (rt)
                    continue
            npt = NounPhraseHelper.try_parse(t, NounPhraseParseAttr.NO, 0, None)
            if (npt is not None and ((npt.end_token.is_value("НАВЫК", None) or npt.end_token.is_value("КОМПЕТЕНЦИЯ", None)))): 
                tt = npt.end_token.next0_
                if (tt is not None and tt.is_value2("И", "ДОСТИЖЕНИЕ")): 
                    tt = tt.next0_.next0_
                rt = ResumeAnalyzer.__parse_list(tt, ad, ResumeItemType.SKILL)
                if (rt is not None): 
                    has_skills = True
                    t = (rt)
                    continue
            if (not has_about and (((t.is_value2("О", "МНЕ") or t.is_value2("О", "СЕБЕ") or t.is_value2("ЛИЧНЫЕ", "КАЧЕСТВА")) or t.is_value2("ДОПОЛНИТЕЛЬНЫЕ", "СВЕДЕНИЯ")))): 
                rt = ResumeAnalyzer.__parse_about_me(t.next0_.next0_, ad)
                if (rt is not None): 
                    has_about = True
                    t = (rt)
                    continue
            if (not has_spec and has_sex and not has_pos): 
                rt = ResumeAnalyzer.__parse_list(t, ad, ResumeItemType.POSITION)
                if (rt is not None): 
                    has_pos = True
                    t = (rt)
                    continue
            if (not has_edu): 
                mt = ResumeAnalyzer.__parse_education(t)
                if (mt is not None): 
                    cur_typ = ResumeItemType.STUDY
                    edu = ResumeItemReferent()
                    has_edu = True
                    edu.typ = ResumeItemType.EDUCATION
                    edu.value = Utils.asObjectOrNull(mt.tag, str)
                    rt = ReferentToken(ad.register_referent(edu), mt.begin_token, mt.end_token)
                    kit.embed_token(rt)
                    t = (rt)
                    continue
            rt = ResumeAnalyzer.__parse_driving(t, ad)
            if (rt is not None): 
                t = (rt)
                continue
        if (not has_edu_org): 
            t = kit.first_token
            first_pass4427 = True
            while True:
                if first_pass4427: first_pass4427 = False
                else: t = t.next0_
                if (not (t is not None)): break
                org0_ = Utils.asObjectOrNull(t.get_referent(), OrganizationReferent)
                if (org0_ is None): 
                    continue
                if (OrgProfile.EDUCATION in org0_.profiles and not (isinstance(org0_.tag, ResumeItemReferent))): 
                    it = ad.register_referent(ResumeItemReferent._new3517(ResumeItemType.STUDY, org0_))
                    rt = ReferentToken(it, t, t)
                    kit.embed_token(rt)
                    break
    
    @staticmethod
    def __parse_sex(t : 'Token', ad : 'AnalyzerData') -> 'ReferentToken':
        if (t.is_value2("ПОЛНЫХ", "ЛЕТ")): 
            t = t.next0_.next0_
            if (t is not None and t.is_char_of(":-")): 
                t = t.next0_
            if (isinstance(t, NumberToken)): 
                age = ResumeItemReferent()
                age.typ = ResumeItemType.AGE
                age.value = t.value
                rt0 = ReferentToken(ad.register_referent(age), t, t)
                t.kit.embed_token(rt0)
                return rt0
        if (not t.is_value("МУЖЧИНА", None) and not t.is_value("ЖЕНЩИНА", None)): 
            return None
        sex = ResumeItemReferent()
        sex.typ = ResumeItemType.SEX
        sex.value = ("муж" if t.is_value("МУЖЧИНА", None) else "жен")
        rt = ReferentToken(ad.register_referent(sex), t, t)
        t.kit.embed_token(rt)
        t = (rt)
        tt = t.next0_
        while tt is not None: 
            if (tt.is_newline_before): 
                break
            if ((isinstance(tt, NumberToken)) and tt.next0_ is not None): 
                if (tt.next0_.is_value("ГОД", None) or tt.next0_.is_value("ЛЕТ", None)): 
                    age = ResumeItemReferent()
                    age.typ = ResumeItemType.AGE
                    age.value = tt.value
                    rt = ReferentToken(ad.register_referent(age), tt, tt.next0_)
                    t.kit.embed_token(rt)
                    t = (rt)
                    break
            tt = tt.next0_
        return rt
    
    @staticmethod
    def __parse_experience(t : 'Token', ad : 'AnalyzerData') -> 'ReferentToken':
        if (not t.is_value2("ОПЫТ", "РАБОТЫ")): 
            return None
        tt = t.next0_
        while tt is not None: 
            if (tt.is_newline_before and not tt.previous.is_char_of(":-")): 
                break
            if ((isinstance(tt, NumberToken)) and tt.next0_ is not None): 
                if (tt.next0_.is_value("ГОД", None) or tt.next0_.is_value("ЛЕТ", None) or tt.next0_.is_value("МЕСЯЦ", None)): 
                    experience = ResumeItemReferent()
                    experience.typ = ResumeItemType.EXPERIENCE
                    experience.value = tt.value
                    if (tt.next0_.is_value("МЕСЯЦ", None)): 
                        experience.value = NumberHelper.double_to_string(round(tt.real_value / (12), 1))
                    tt1 = tt.next0_
                    if ((isinstance(tt1.next0_, NumberToken)) and tt1.next0_.next0_ is not None and tt1.next0_.next0_.is_value("МЕСЯЦ", None)): 
                        d = round(tt.real_value + ((tt1.next0_.real_value / (12))), 1)
                        experience.value = NumberHelper.double_to_string(d)
                        tt1 = tt1.next0_.next0_
                    rt = ReferentToken(ad.register_referent(experience), tt, tt1)
                    t.kit.embed_token(rt)
                    return rt
            tt = tt.next0_
        return None
    
    @staticmethod
    def __parse_education(t : 'Token') -> 'MetaToken':
        hi = False
        middl = False
        prof = False
        spec = False
        tech = False
        neok = False
        keyword_ = False
        t0 = t
        t1 = t
        while t is not None: 
            if (t0 != t and t.is_newline_before): 
                if (t.previous is not None and t.previous.is_char_of(":-")): 
                    pass
                else: 
                    break
            if (t.is_value("СРЕДНИЙ", None) or t.is_value("СРЕДНЕ", None) or t.is_value("СРЕДН", None)): 
                middl = True
            elif (t.is_value("ВЫСШИЙ", None) or t.is_value("ВЫСШ", None)): 
                hi = True
            elif (t.is_value("НЕОКОНЧЕННЫЙ", None)): 
                neok = True
            elif (t.is_value("ПРОФЕССИОНАЛЬНЫЙ", None) or t.is_value("ПРОФ", None) or t.is_value("ПРОФИЛЬНЫЙ", None)): 
                prof = True
            elif ((t.is_value("СПЕЦИАЛЬНЫЙ", None) or t.is_value("СПЕЦ", None) or t.is_value2("ПО", "СПЕЦИАЛЬНОСТЬ")) or t.is_value2("ПО", "НАПРАВЛЕНИЕ")): 
                spec = True
            elif ((t.is_value("ТЕХНИЧЕСКИЙ", None) or t.is_value("ТЕХ", None) or t.is_value("ТЕХН", None)) or t.is_value("ТЕХНИЧ", None)): 
                tech = True
            elif (t.is_value("ОБРАЗОВАНИЕ", None)): 
                keyword_ = True
                t1 = t
            elif (t.length_char > 1): 
                break
            t = t.next0_
        if (not keyword_): 
            return None
        if (not hi and not middl): 
            if ((spec or prof or tech) or neok): 
                middl = True
            else: 
                return None
        val = ("ВО" if hi else "СО")
        if (spec): 
            val += ",спец"
        if (prof): 
            val += ",проф"
        if (tech): 
            val += ",тех"
        if (neok): 
            val += ",неоконч"
        return MetaToken._new663(t0, t1, val)
    
    @staticmethod
    def __parse_moral(t : 'Token') -> 'MetaToken':
        tok = VacanceToken.M_TERMINS.try_parse(t, TerminParseAttr.NO)
        if (tok is None or tok.termin.tag2 is not None): 
            return None
        ty = Utils.valToEnum(tok.termin.tag, VacanceTokenType)
        if (ty != VacanceTokenType.MORAL): 
            return None
        val = "{0}{1}".format(tok.termin.canonic_text[0], tok.termin.canonic_text[1:].lower())
        t1 = tok.end_token
        tt = tok.end_token.next0_
        while tt is not None: 
            if (tt.whitespaces_before_count > 2): 
                break
            if (VacanceToken.M_TERMINS.try_parse(tt, TerminParseAttr.NO) is not None): 
                break
            npt = NounPhraseHelper.try_parse(tt, Utils.valToEnum((NounPhraseParseAttr.PARSEPREPOSITION) | (NounPhraseParseAttr.PARSEPRONOUNS), NounPhraseParseAttr), 0, None)
            if (npt is None): 
                break
            t1 = npt.end_token
            tt = t1
            tt = tt.next0_
        if (t1.end_char > tok.end_char): 
            val = "{0} {1}".format(val, MiscHelper.get_text_value(tok.end_token.next0_, t1, Utils.valToEnum((GetTextAttr.KEEPQUOTES) | (GetTextAttr.KEEPREGISTER), GetTextAttr)))
        return MetaToken._new663(t, t1, val)
    
    @staticmethod
    def __parse_driving(t : 'Token', ad : 'AnalyzerData') -> 'ReferentToken':
        if (t is None): 
            return None
        t1 = None
        if (t.is_value("НАЛИЧИЕ", None) and t.next0_ is not None): 
            t = t.next0_
        if ((t.is_value2("ВОДИТЕЛЬСКИЕ", "ПРАВА") or t.is_value2("ПРАВА", "КАТЕГОРИИ") or t.is_value2("ВОДИТЕЛЬСКОЕ", "УДОСТОВЕРЕНИЕ")) or t.is_value2("УДОСТОВЕРЕНИЕ", "ВОДИТЕЛЯ") or t.is_value2("ПРАВА", "ВОДИТЕЛЯ")): 
            t1 = t.next0_.next0_
        if (t1 is None): 
            return None
        t0 = t
        val = None
        t = t1
        first_pass4428 = True
        while True:
            if first_pass4428: first_pass4428 = False
            else: t = t.next0_
            if (not (t is not None)): break
            if ((t.is_hiphen or t.is_table_control_char or t.is_char_of(":.")) or t.is_value("КАТЕГОРИЯ", None) or t.is_value("КАТ", None)): 
                continue
            if ((isinstance(t, TextToken)) and t.length_char <= 3 and t.chars.is_letter): 
                val = t.term
                t1 = t
                t = t.next0_
                first_pass4429 = True
                while True:
                    if first_pass4429: first_pass4429 = False
                    else: t = t.next0_
                    if (not (t is not None)): break
                    if (t.whitespaces_before_count > 2): 
                        break
                    elif (t.is_char('.') or t.is_comma_and): 
                        continue
                    elif (t.length_char == 1 and t.chars.is_all_upper and t.chars.is_letter): 
                        val = "{0}{1}".format(val, t.term)
                        t1 = t
                    else: 
                        break
                val = val.replace("А", "A").replace("В", "B").replace("С", "C")
                break
            break
        if (val is None): 
            return None
        drv = ResumeItemReferent()
        drv.typ = ResumeItemType.DRIVINGLICENSE
        drv.value = val
        rt = ReferentToken(ad.register_referent(drv), t0, t1)
        t0.kit.embed_token(rt)
        return rt
    
    @staticmethod
    def __parse_onto(t : 'Token') -> 'MetaToken':
        if (t is None): 
            return None
        if (t.kit.ontology is None): 
            return None
        lii = t.kit.ontology.attach_token(ResumeAnalyzer.ANALYZER_NAME, t)
        if (lii is None or len(lii) == 0): 
            return None
        if (not (isinstance(lii[0].item.referent, ResumeItemReferent))): 
            return None
        val = lii[0].item.referent.value
        val = "{0}{1}".format(val[0], val[1:].lower())
        return MetaToken._new663(t, lii[0].end_token, val)
    
    @staticmethod
    def _start_of_item(t : 'Token') -> bool:
        if (t is None): 
            return False
        if (t.is_table_control_char and t.next0_ is not None): 
            t = t.next0_
        if (t.is_newline_before): 
            pass
        elif (t.previous is not None and t.previous.is_table_control_char and t.previous.is_newline_before): 
            pass
        else: 
            return False
        if (t.is_value2("О", "МНЕ") or t.is_value2("О", "СЕБЕ") or t.is_value2("ДОПОЛНИТЕЛЬНЫЕ", "СВЕДЕНИЯ")): 
            return True
        if (t.is_value("ИНТЕРЕСНЫ", None)): 
            return True
        npt = NounPhraseHelper.try_parse(t, NounPhraseParseAttr.NO, 0, None)
        if (npt is not None): 
            tt = npt.end_token
            if ((((tt.is_value("КОНТАКТ", None) or tt.is_value("ТЕЛЕФОН", None) or tt.is_value("РЕЗЮМЕ", None)) or tt.is_value("ГРАФИК", None) or tt.is_value("ЗАНЯТОСТЬ", None)) or tt.is_value("ОПЫТ", None) or tt.is_value("ОБРАЗОВАНИЕ", None)) or tt.is_value("ИНФОРМАЦИЯ", None)): 
                return True
            if (tt.is_value("НАВЫК", None)): 
                if (t.is_value("КЛЮЧЕВОЙ", None) or t.is_value("ПРОФЕССИОНАЛЬНЫЙ", None)): 
                    return True
            if (tt.is_value("КОМПЕТЕНЦИЯ", None)): 
                return True
        return False
    
    @staticmethod
    def __parse_list(t : 'Token', ad : 'AnalyzerData', typ : 'ResumeItemType') -> 'ReferentToken':
        rt = None
        spec = None
        while t is not None: 
            if (not t.is_char(':') and not t.is_table_control_char): 
                break
            t = t.next0_
        if (t is None): 
            return None
        t0 = t
        first_pass4430 = True
        while True:
            if first_pass4430: first_pass4430 = False
            else: t = t.next0_
            if (not (t is not None)): break
            if (t.is_newline_before): 
                if (t.newlines_before_count > 1 and t != t0): 
                    break
                if (ResumeAnalyzer._start_of_item(t)): 
                    break
                if (t == t0 and typ == ResumeItemType.POSITION): 
                    pass
                elif (typ == ResumeItemType.SKILL or typ == ResumeItemType.SPECIALITY): 
                    pass
                else: 
                    break
            if (t.is_char_of(";,")): 
                continue
            if (BracketHelper.can_be_start_of_sequence(t, True, False)): 
                br = BracketHelper.try_parse(t, BracketParseAttr.NO, 100)
                if (br is not None): 
                    spec = ResumeItemReferent()
                    spec.typ = typ
                    spec.value = ResumeAnalyzer.__corr_value(MiscHelper.get_text_value(t.next0_, br.end_token.previous, Utils.valToEnum((GetTextAttr.KEEPQUOTES) | (GetTextAttr.KEEPREGISTER), GetTextAttr)))
                    rt = ReferentToken(ad.register_referent(spec), t, br.end_token)
                    t.kit.embed_token(rt)
                    t = (rt)
                    continue
            if (typ == ResumeItemType.SKILL): 
                tok = VacanceToken.M_SKILL_ITEMS.try_parse(t, TerminParseAttr.NO)
                if (tok is not None): 
                    spec = ResumeItemReferent()
                    spec.typ = typ
                    spec.value = tok.termin.canonic_text
                    rt = ReferentToken(ad.register_referent(spec), t, tok.end_token)
                    t.kit.embed_token(rt)
                    t = (rt)
                    continue
            t1 = t
            tt = t.next0_
            first_pass4431 = True
            while True:
                if first_pass4431: first_pass4431 = False
                else: tt = tt.next0_
                if (not (tt is not None)): break
                if (tt.is_newline_before): 
                    if (tt.previous.is_hiphen): 
                        continue
                    break
                if (tt.is_char_of(";,") or MiscHelper.can_be_start_of_sentence(tt)): 
                    break
                if (tt.is_char('(')): 
                    br = BracketHelper.try_parse(tt, BracketParseAttr.NO, 100)
                    if (br is not None): 
                        tt = br.end_token
                t1 = tt
            if (t1 is None): 
                break
            if (t1.next0_ is None): 
                return None
            if (typ == ResumeItemType.POSITION): 
                ok = False
                tt = t1.next0_
                while tt is not None: 
                    if (tt.is_newline_before): 
                        if (isinstance(tt.get_referent(), MoneyReferent)): 
                            ok = True
                        elif (tt.is_value("СПЕЦИАЛИЗАЦИЯ", None)): 
                            ok = True
                        break
                    tt = tt.next0_
                if (not ok): 
                    return None
            rt1 = ResumeAnalyzer.__parse_driving(t, ad)
            if (rt1 is not None): 
                t = (rt1)
                rt = rt1
                continue
            mt = ResumeAnalyzer.__parse_moral(t)
            if (mt is not None): 
                mor = ResumeItemReferent()
                mor.typ = ResumeItemType.MORAL
                mor.value = ResumeAnalyzer.__corr_value(Utils.asObjectOrNull(mt.tag, str))
                rt = ReferentToken(ad.register_referent(mor), t, mt.end_token)
            else: 
                spec = ResumeItemReferent()
                spec.typ = typ
                if ((t.length_char == 1 and (isinstance(t, TextToken)) and not t.chars.is_letter) and (t.end_char < t1.begin_char)): 
                    t = t.next0_
                while t is not None: 
                    if (t.is_value("НАВЫК", None) or t.is_table_control_char or ((t.length_char == 1 and (isinstance(t, TextToken)) and not t.chars.is_letter))): 
                        pass
                    else: 
                        break
                    t = t.next0_
                if (t is None): 
                    break
                if (t.end_char > t1.end_char): 
                    t = t1
                    continue
                spec.value = ResumeAnalyzer.__corr_value(MiscHelper.get_text_value(t, t1, Utils.valToEnum((GetTextAttr.KEEPQUOTES) | (GetTextAttr.KEEPREGISTER), GetTextAttr)))
                if (spec.value == "другое" or spec.value == "языки" or spec.value == "курсы"): 
                    t = t1
                    continue
                tt = t
                while tt is not None and (tt.end_char < t1.end_char): 
                    npt = NounPhraseHelper.try_parse(tt, NounPhraseParseAttr.NO, 0, None)
                    if (npt is not None and len(npt.adjectives) > 0): 
                        if (npt.end_token.is_value("УРОВЕНЬ", None) or npt.end_token.is_value("НАВЫК", None) or npt.end_token.is_value("ЗНАНИЕ", None)): 
                            if (tt != t): 
                                spec.misc = npt.get_normal_case_text(None, MorphNumber.UNDEFINED, MorphGender.UNDEFINED, False).lower()
                                if (tt.previous.is_char_of("-(")): 
                                    tt = tt.previous
                                if (tt.previous.begin_char >= t.begin_char): 
                                    tok = VacanceToken.M_SKILL_ITEMS.try_parse(t, TerminParseAttr.NO)
                                    if (tok is not None): 
                                        spec.value = tok.termin.canonic_text
                                    else: 
                                        spec.value = ResumeAnalyzer.__corr_value(MiscHelper.get_text_value(t, tt.previous, Utils.valToEnum((GetTextAttr.KEEPQUOTES) | (GetTextAttr.KEEPREGISTER), GetTextAttr)))
                            elif (npt.end_char < t1.end_char): 
                                spec.misc = npt.get_normal_case_text(None, MorphNumber.UNDEFINED, MorphGender.UNDEFINED, False).lower()
                                tok = VacanceToken.M_SKILL_ITEMS.try_parse(npt.end_token.next0_, TerminParseAttr.NO)
                                if (tok is not None): 
                                    spec.value = tok.termin.canonic_text
                                else: 
                                    spec.value = ResumeAnalyzer.__corr_value(MiscHelper.get_text_value(npt.end_token.next0_, t1, Utils.valToEnum((GetTextAttr.FIRSTNOUNGROUPTONOMINATIVE) | (GetTextAttr.KEEPQUOTES) | (GetTextAttr.KEEPREGISTER), GetTextAttr)))
                            break
                    tt = tt.next0_
                if (Utils.isNullOrEmpty(spec.value)): 
                    t = t1
                    continue
                rt = ReferentToken(ad.register_referent(spec), t, t1)
            t.kit.embed_token(rt)
            t = (rt)
        return rt
    
    @staticmethod
    def __corr_value(val : str) -> str:
        if (Utils.isNullOrEmpty(val) or (len(val) < 3)): 
            return val
        val = val.replace(" - ", "-")
        if (val.endswith(".")): 
            val = val[0:0+len(val) - 1]
        if (str.isupper(val[0]) and str.islower(val[1]) and (ord(val[0])) > 0x100): 
            val = "{0}{1}".format(str.lower(val[0]), val[1:])
        return val
    
    @staticmethod
    def __parse_about_me(t : 'Token', ad : 'AnalyzerData') -> 'ReferentToken':
        t0 = t
        rt = None
        first_pass4432 = True
        while True:
            if first_pass4432: first_pass4432 = False
            else: t = t.next0_
            if (not (t is not None)): break
            if (t.is_newline_before): 
                if (ResumeAnalyzer.__parse_education(t) is not None): 
                    break
                if (ResumeAnalyzer._start_of_item(t)): 
                    break
            mt = ResumeAnalyzer.__parse_moral(t)
            if (mt is not None): 
                mor = ResumeItemReferent()
                mor.typ = ResumeItemType.MORAL
                mor.value = ResumeAnalyzer.__corr_value(Utils.asObjectOrNull(mt.tag, str))
                rt = ReferentToken(ad.register_referent(mor), t, mt.end_token)
                t.kit.embed_token(rt)
                t = (rt)
                continue
            mt = ResumeAnalyzer.__parse_onto(t)
            if (mt is not None): 
                mor = ResumeItemReferent()
                mor.typ = ResumeItemType.SKILL
                mor.value = ResumeAnalyzer.__corr_value(Utils.asObjectOrNull(mt.tag, str))
                rt = ReferentToken(ad.register_referent(mor), t, mt.end_token)
                t.kit.embed_token(rt)
                t = (rt)
                continue
        return rt
    
    @staticmethod
    def _parse_org(t : 'Token', typ : 'ResumeItemType') -> 'ReferentToken':
        t0 = t
        if (t is None): 
            return None
        while t is not None: 
            if (((t.is_char_of(":-") or t.is_table_control_char or t.is_value("ПЕРИОД", None)) or t.is_value("РАБОТА", None) or t.is_value("ВРЕМЯ", None)) or t.is_value("МЕСТО", None)): 
                pass
            elif (((isinstance(t, NumberToken)) and t.is_newline_before and t.next0_ is not None) and t.next0_.is_char_of(".)")): 
                t = t.next0_
            else: 
                break
            t = t.next0_
        if (t is None): 
            return None
        org0_ = None
        post = None
        post_attr = None
        dt = t.get_referent()
        t1 = None
        if ((isinstance(dt, DateReferent)) or (isinstance(dt, DateRangeReferent))): 
            pass
        elif ((isinstance(dt, OrganizationReferent)) and t0.is_newline_before and t.next0_ is not None): 
            org0_ = (Utils.asObjectOrNull(dt, OrganizationReferent))
            dt = (None)
            t = t.next0_
            if (t.is_char('(') and t.next0_ is not None): 
                t = t.next0_
            if ((isinstance(t.get_referent(), DateReferent)) or (isinstance(t.get_referent(), DateRangeReferent))): 
                dt = t.get_referent()
                t1 = t
                t = t.next0_
                if (t is not None and t.is_char(')')): 
                    t1 = t
                    t = t.next0_
            pat = PersonAttrToken.try_attach(t, PersonAttrToken.PersonAttrAttachAttrs.NO)
            if (pat is None and t is not None and (isinstance(t.get_referent(), PersonPropertyReferent))): 
                pat = PersonAttrToken._new3525(t, t, Utils.asObjectOrNull(t.get_referent(), PersonPropertyReferent))
            if (pat is None or pat.prop_ref is None): 
                if (dt is not None and typ != ResumeItemType.UNDEFINED): 
                    pass
                else: 
                    return None
            else: 
                post_attr = pat
                dt = (Utils.asObjectOrNull(pat.prop_ref.get_slot_value(PersonPropertyReferent.ATTR_REF), Referent))
                if (dt is None and pat.prop_ref.higher is not None): 
                    dt = (Utils.asObjectOrNull(pat.prop_ref.higher.get_slot_value(PersonPropertyReferent.ATTR_REF), Referent))
                t1 = pat.end_token
                if ((isinstance(dt, DateReferent)) or (isinstance(dt, DateRangeReferent))): 
                    pat.prop_ref.add_slot(PersonPropertyReferent.ATTR_REF, None, True, 0)
                    if (pat.prop_ref.higher is not None): 
                        pat.prop_ref.higher.add_slot(PersonPropertyReferent.ATTR_REF, None, True, 0)
                elif (dt is None): 
                    if (pat.end_token.next0_ is None): 
                        return None
                    dt = pat.end_token.next0_.get_referent()
                    if ((isinstance(dt, DateReferent)) or (isinstance(dt, DateRangeReferent))): 
                        pass
                    else: 
                        return None
                    t1 = pat.end_token.next0_
        else: 
            return None
        cou = 0
        if (t1 is None): 
            t = t.next0_
            first_pass4433 = True
            while True:
                if first_pass4433: first_pass4433 = False
                else: t = t.next0_
                if (not (t is not None and (cou < 3))): break
                if (((post is not None or post_attr is not None)) and org0_ is not None): 
                    break
                if (t.is_newline_before): 
                    cou += 1
                r = Utils.asObjectOrNull(t.get_referent(), OrganizationReferent)
                if (r is not None): 
                    if (org0_ is not None): 
                        if (r.higher == org0_ or ((r.higher is not None and r.higher == org0_))): 
                            pass
                        else: 
                            break
                    org0_ = r
                    t1 = t
                    cou = 0
                    continue
                p = Utils.asObjectOrNull(t.get_referent(), PersonPropertyReferent)
                if (p is not None and post is None): 
                    post = p
                    t1 = t
                    cou = 0
                    continue
                if (post is None and post_attr is None): 
                    if (t.is_value("ТЕХНИК", None)): 
                        pass
                    pat = PersonAttrToken.try_attach(t, PersonAttrToken.PersonAttrAttachAttrs.NO)
                    if (pat is not None and pat.prop_ref is not None): 
                        post_attr = pat
                        t1 = pat.end_token
                        t = t1
                        if (org0_ is None): 
                            if (pat.higher_prop_ref is not None): 
                                org0_ = (Utils.asObjectOrNull(pat.higher_prop_ref.prop_ref.get_slot_value(PersonPropertyReferent.ATTR_REF), OrganizationReferent))
                            else: 
                                org0_ = (Utils.asObjectOrNull(pat.prop_ref.get_slot_value(PersonPropertyReferent.ATTR_REF), OrganizationReferent))
                        cou = 0
                        continue
                if ((isinstance(t.get_referent(), DateReferent)) or (isinstance(t.get_referent(), DateRangeReferent))): 
                    break
                if ((isinstance(t, NumberToken)) and t.is_newline_before): 
                    if (t.next0_ is not None and t.next0_.is_char_of(".)")): 
                        break
                    num = Utils.asObjectOrNull(t, NumberToken)
                    if (num.int_value is not None): 
                        if (num.int_value >= 2000 and num.int_value <= 2050): 
                            break
        if (org0_ is None): 
            return None
        if (typ == ResumeItemType.UNDEFINED and OrgProfile.EDUCATION in org0_.profiles): 
            typ = ResumeItemType.STUDY
        res = ResumeItemReferent._new3526((ResumeItemType.ORGANIZATION if typ == ResumeItemType.UNDEFINED else typ))
        res.date_range = dt
        res.ref = org0_
        if (post is None and post_attr is not None): 
            post = post_attr.prop_ref
        if (post is not None): 
            res.value = post.to_string_ex(True, None, 0)
        return ReferentToken(res, t0, t1)
    
    @staticmethod
    def _parse_org2(t : 'Token', ad : 'AnalyzerData', links : typing.List['ReferentToken']=None) -> 'ReferentToken':
        if (t is None): 
            return None
        if (not t.is_value2("ОПЫТ", "РАБОТЫ")): 
            return None
        t = t.next0_
        while t is not None: 
            if (t.is_newline_before): 
                break
            t = t.next0_
        if (t is None): 
            return None
        res = None
        first_pass4434 = True
        while True:
            if first_pass4434: first_pass4434 = False
            else: t = t.next0_
            if (not (t is not None)): break
            if (ResumeAnalyzer._start_of_item(t)): 
                break
            if (not t.is_newline_before): 
                continue
            prop = Utils.asObjectOrNull(t.get_referent(), PersonPropertyReferent)
            org0_ = None
            if (prop is None): 
                pr = PersonAttrToken.try_attach(t, PersonAttrToken.PersonAttrAttachAttrs.NO)
                if (pr is not None): 
                    prop = pr.prop_ref
                    t = pr.end_token
            if (prop is None): 
                if (res is None): 
                    break
                continue
            t0 = t
            if (t.next0_ is not None and ((t.next0_.is_value("В", None) or t.next0_.is_char_of("-:,")))): 
                t = t.next0_
            org0_ = (Utils.asObjectOrNull(prop.get_slot_value(PersonPropertyReferent.ATTR_REF), OrganizationReferent))
            if (org0_ is None): 
                org0_ = (Utils.asObjectOrNull(t.get_referent(), OrganizationReferent))
            if (org0_ is None): 
                break
            dt_tok = None
            t = t.next0_
            while t is not None: 
                if ((isinstance(t.get_referent(), DateReferent)) or (isinstance(t.get_referent(), DateRangeReferent))): 
                    dt_tok = (Utils.asObjectOrNull(t, ReferentToken))
                    break
                if (t.length_char > 1): 
                    break
                t = t.next0_
            if (dt_tok is None): 
                break
            it = ResumeItemReferent._new3526(ResumeItemType.ORGANIZATION)
            it.date_range = dt_tok.referent
            it.ref = org0_
            it.value = prop.to_string_ex(True, None, 0)
            org0_.tag = (it)
            if (links is None): 
                res = ReferentToken(ad.register_referent(it), t0, dt_tok)
                t0.kit.embed_token(res)
                t = (res)
            else: 
                links.append(ReferentToken(it, t0, dt_tok))
                t = (dt_tok)
        return res
    
    @staticmethod
    def __check_geo(t : 'Token') -> bool:
        if (t is None): 
            return False
        if (t.is_value2("УКАЗАН", "ПРИМЕРНЫЙ")): 
            return True
        tt = t
        while tt is not None: 
            if (tt != t and tt.is_newline_before): 
                break
            r = tt.get_referent()
            if ((isinstance(r, GeoReferent)) or (isinstance(r, StreetReferent)) or (isinstance(r, AddressReferent))): 
                return True
            if (tt.is_value("ГОТОВ", None) or tt.is_value("ПЕРЕЕЗД", None) or tt.is_value("КОМАНДИРОВКА", None)): 
                return True
            tt = tt.next0_
        return False
    
    def process_ontology_item(self, begin : 'Token') -> 'ReferentToken':
        t = begin
        while t is not None: 
            if (t.next0_ is None): 
                re = ResumeItemReferent()
                re.value = MiscHelper.get_text_value(begin, t, GetTextAttr.NO)
                return ReferentToken(re, begin, t)
            t = t.next0_
        return None
    
    __m_initialized = False
    
    __m_lock = None
    
    @staticmethod
    def initialize() -> None:
        with ResumeAnalyzer.__m_lock: 
            if (ResumeAnalyzer.__m_initialized): 
                return
            ResumeAnalyzer.__m_initialized = True
            MetaResume.initialize()
            VacanceToken.initialize()
            ProcessorService.register_analyzer(ResumeAnalyzer())
    
    # static constructor for class ResumeAnalyzer
    @staticmethod
    def _static_ctor():
        ResumeAnalyzer.__m_lock = threading.Lock()

ResumeAnalyzer._static_ctor()