Clazz.declarePackage ("JS"); Clazz.load (["java.util.Hashtable"], "JS.SmilesParser", ["java.lang.Character", "$.Float", "JU.Lst", "$.PT", "$.SB", "JS.InvalidSmilesException", "$.SmilesAtom", "$.SmilesBond", "$.SmilesMeasure", "$.SmilesSearch", "$.SmilesStereo", "JU.Elements", "$.Logger"], function () { c$ = Clazz.decorateAsClass (function () { this.connections = null; this.htMeasures = null; this.flags = 0; this.isSmarts = false; this.isBioSequence = false; this.bioType = '\0'; this.braceCount = 0; this.branchLevel = 0; this.componentCount = 0; this.componentParenCount = 0; this.ignoreStereochemistry = false; this.bondDirectionPaired = true; this.isTarget = false; Clazz.instantialize (this, arguments); }, JS, "SmilesParser"); Clazz.prepareFields (c$, function () { this.connections = new java.util.Hashtable (); this.htMeasures = new java.util.Hashtable (); }); c$.newSearch = Clazz.defineMethod (c$, "newSearch", function (pattern, isSmarts, isTarget) { return ( new JS.SmilesParser (isSmarts, isTarget)).parse (pattern); }, "~S,~B,~B"); Clazz.makeConstructor (c$, function (isSmarts, isTarget) { this.isSmarts = isSmarts; this.isTarget = isTarget; }, "~B,~B"); Clazz.defineMethod (c$, "parse", function (pattern) { if (pattern == null) throw new JS.InvalidSmilesException ("expression must not be null"); var search = new JS.SmilesSearch (); if (pattern.indexOf ("$(select") >= 0) pattern = this.parseNested (search, pattern, "select"); var ret = Clazz.newIntArray (1, 0); pattern = JS.SmilesParser.extractFlags (pattern, ret); this.flags = ret[0]; this.ignoreStereochemistry = ((this.flags & 32) == 32); search.setFlags (this.flags); if (pattern.indexOf ("$") >= 0) pattern = this.parseVariables (pattern); if (this.isSmarts && pattern.indexOf ("[$") >= 0) pattern = this.parseVariableLength (pattern); if (pattern.indexOf ("||") < 0) return this.getSubsearch (search, pattern, this.flags); var patterns = JU.PT.split (pattern, "||"); var toDo = ""; search.subSearches = new Array (patterns.length); for (var i = 0; i < patterns.length; i++) { var key = "|" + patterns[i] + "|"; if (toDo.indexOf (key) < 0) { search.subSearches[i] = this.getSubsearch (search, patterns[i], this.flags); toDo += key; }} return search; }, "~S"); Clazz.defineMethod (c$, "parseVariableLength", function (pattern) { var sout = new JU.SB (); var len = pattern.length - 1; var nParen = 0; var haveInternalOr = false; for (var i = 0; i < len; i++) { switch (pattern.charAt (i)) { case '(': nParen++; break; case ')': nParen--; break; case '|': if (nParen > 0) { haveInternalOr = true; if (pattern.charAt (i + 1) == '|') { pattern = pattern.substring (0, i) + pattern.substring (i + 1); len--; }}break; } } if (pattern.indexOf ("||") >= 0) { var patterns = JU.PT.split (pattern, "||"); for (var i = 0; i < patterns.length; i++) sout.append ("||").append (this.parseVariableLength (patterns[i])); } else { var pt = -1; var ret = Clazz.newIntArray (1, 0); var isOK = true; var bracketed = null; while ((pt = pattern.indexOf ("[$", pt + 1)) >= 0) { var pt0 = pt; var min = -2147483648; var max = -2147483648; pt = JS.SmilesParser.getDigits (pattern, pt + 2, ret); min = ret[0]; if (min != -2147483648) { if (JS.SmilesParser.getChar (pattern, pt) == '-') { pt = JS.SmilesParser.getDigits (pattern, pt + 1, ret); max = ret[0]; }}if (JS.SmilesParser.getChar (pattern, pt) != '(') continue; bracketed = JS.SmilesParser.getSubPattern (pattern, pt0, '['); if (!bracketed.endsWith (")")) continue; var pt1 = pt0 + bracketed.length + 2; var repeat = JS.SmilesParser.getSubPattern (pattern, pt, '('); var pt2 = pt; bracketed = JS.SmilesParser.getSubPattern (pattern, pt, '['); pt += 1 + repeat.length; if (repeat.indexOf (':') >= 0 && repeat.indexOf ('|') < 0) { var parenCount = 0; var n = repeat.length; var ptColon = -1; for (var i = 0; i < n; i++) { switch (repeat.charAt (i)) { case '[': case '(': parenCount++; break; case ')': case ']': parenCount--; break; case '.': if (ptColon >= 0 && parenCount == 0) n = i; break; case ':': if (ptColon < 0 && parenCount == 0) ptColon = i; break; } } if (ptColon > 0) repeat = repeat.substring (0, ptColon) + "(" + repeat.substring (ptColon, n) + ")" + repeat.substring (n); }if (min == -2147483648) { var ptOr = repeat.indexOf ("|"); if (ptOr >= 0) return this.parseVariableLength (pattern.substring (0, pt0) + "[$1" + pattern.substring (pt2, pt2 + ptOr + 1) + ")]" + pattern.substring (pt1) + "||" + pattern.substring (0, pt0) + "[$1(" + pattern.substring (pt2 + ptOr + 2) + pattern.substring (pt1)); continue; }if (max == -2147483648) max = min; if (repeat.indexOf ("|") >= 0) repeat = "[$(" + repeat + ")]"; for (var i = min; i <= max; i++) { var sb = new JU.SB (); sb.append ("||").append (pattern.substring (0, pt0)); for (var j = 0; j < i; j++) sb.append (repeat); sb.append (pattern.substring (pt1)); sout.appendSB (sb); } } if (!isOK) throw new JS.InvalidSmilesException ("bad variable expression: " + bracketed); }return (haveInternalOr ? this.parseVariableLength (sout.substring (2)) : sout.length () < 2 ? pattern : sout.substring (2)); }, "~S"); Clazz.defineMethod (c$, "getSubsearch", function (parent, pattern, flags) { this.htMeasures = new java.util.Hashtable (); var search = new JS.SmilesSearch (); search.setTop (parent); search.isSmarts = this.isSmarts; search.pattern = pattern; search.setFlags (flags); if (pattern.indexOf ("$(") >= 0) pattern = this.parseNested (search, pattern, ""); this.parseSmiles (search, pattern, null, false); if (this.braceCount != 0) throw new JS.InvalidSmilesException ("unmatched '{'"); if (!this.connections.isEmpty ()) throw new JS.InvalidSmilesException ("Open connection"); search.set (); if (this.isSmarts) for (var i = search.ac; --i >= 0; ) this.checkNested (search, search.patternAtoms[i], flags); else if (!this.isBioSequence) search.elementCounts[1] = search.getMissingHydrogenCount (); if (!this.ignoreStereochemistry && !this.isTarget) this.fixChirality (search); return search; }, "JS.SmilesSearch,~S,~N"); Clazz.defineMethod (c$, "checkNested", function (search, atom, flags) { if (atom.iNested > 0) { var o = search.getNested (atom.iNested); if (Clazz.instanceOf (o, String)) { var s = o; if (s.startsWith ("select")) return; if (s.charAt (0) != '~' && atom.bioType != '\0') s = "~" + atom.bioType + "~" + s; var nested = this.getSubsearch (search, s, flags); if (nested.ac > 0 && nested.patternAtoms[0].selected) atom.selected = true; search.setNested (atom.iNested, nested); }}for (var i = 0; i < atom.nSubAtoms; i++) this.checkNested (search, atom.subAtoms[i], flags); }, "JS.SmilesSearch,JS.SmilesAtom,~N"); Clazz.defineMethod (c$, "fixChirality", function (search) { for (var i = search.ac; --i >= 0; ) { var sAtom = search.patternAtoms[i]; if (sAtom.stereo != null) sAtom.stereo.fixStereo (sAtom); } }, "JS.SmilesSearch"); Clazz.defineMethod (c$, "parseSmiles", function (search, pattern, currentAtom, isBranchAtom) { var ret = Clazz.newIntArray (1, 0); var pt = 0; var ch; var bond = null; var wasMeasure = false; var wasBranch = false; loop : while (pattern != null && pattern.length != 0) { var index = 0; if (currentAtom == null) { index = this.checkBioType (pattern, 0); if (index == pattern.length) pattern += "*"; if (this.isBioSequence) search.needAromatic = search.top.needAromatic = false; }ch = JS.SmilesParser.getChar (pattern, index); var haveOpen = this.checkBrace (search, ch, '{'); if (haveOpen) ch = JS.SmilesParser.getChar (pattern, ++index); if (ch == '(') { var subString = JS.SmilesParser.getSubPattern (pattern, index, '('); var isMeasure = (JS.SmilesParser.getChar (pattern, index + 1) == '.'); if (currentAtom == null) { if (isMeasure || !this.isSmarts) throw new JS.InvalidSmilesException ("No previous atom for measure"); search.haveComponents = true; do { this.componentCount++; this.componentParenCount++; ch = JS.SmilesParser.getChar (pattern = pattern.substring (1), 0); } while (ch == '('); if (!haveOpen && (haveOpen = this.checkBrace (search, ch, '{')) == true) ch = JS.SmilesParser.getChar (pattern = pattern.substring (1), 0); } else { wasMeasure = wasBranch = false; if (subString.startsWith (".")) { this.parseMeasure (search, subString.substring (1), currentAtom); wasMeasure = true; } else if (subString.length == 0 && this.isBioSequence) { currentAtom.notCrossLinked = true; } else { this.branchLevel++; this.parseSmiles (search, subString, currentAtom, true); wasBranch = true; this.branchLevel--; }index = subString.length + 2; ch = JS.SmilesParser.getChar (pattern, index); if (ch == '}' && this.checkBrace (search, ch, '}')) index++; ch = '\0'; }}if (ch != '\0') { pt = index; out : while (ch != '\0') { switch (JS.SmilesBond.isBondType (ch, this.isSmarts, this.isBioSequence)) { case 1: break; case 0: break out; case -1: if (!((JU.PT.isDigit (JS.SmilesParser.getChar (pattern, ++index)) && index++ > 0 ? JU.PT.isDigit (JS.SmilesParser.getChar (pattern, index++)) : true) && (ch = JS.SmilesParser.getChar (pattern, index)) == '-')) throw new JS.InvalidSmilesException ("malformed atropisomerism bond ^nn- or ^^nn-"); continue; } ch = JS.SmilesParser.getChar (pattern, ++index); } ch = JS.SmilesParser.getChar (pattern, index); if (ch == ')') { switch (ch = JS.SmilesParser.getChar (pattern, ++index)) { case '\0': case ')': case '.': pattern = pattern.substring (index); this.componentParenCount--; if (this.componentParenCount >= 0) continue loop; } throw new JS.InvalidSmilesException ("invalid continuation after component grouping (SMARTS).(SMARTS)"); }bond = this.parseBond (search, null, pattern.substring (pt, index), null, currentAtom, false, isBranchAtom, index - pt, ret); if (haveOpen && bond.order != -1) ch = JS.SmilesParser.getChar (pattern, index = pt); if (this.checkBrace (search, ch, '{')) ch = JS.SmilesParser.getChar (pattern, ++index); switch (ch) { case '~': if (bond.order == 0) { index = this.checkBioType (pattern, index); if (index == pattern.length) pattern += "*"; }break; case '(': do { this.componentCount++; this.componentParenCount++; ch = JS.SmilesParser.getChar (pattern, ++index); } while (ch == '('); break; case '\0': if (bond.order == 0) return; } var isConnect = (JU.PT.isDigit (ch) || ch == '%'); var isAtom = (!isConnect && (ch == '_' || ch == '[' || ch == '*' || JU.PT.isLetter (ch))); if (isConnect) { if (wasMeasure || wasBranch) throw new JS.InvalidSmilesException ("connection number must immediately follow its connecting atom"); index = JS.SmilesParser.getRingNumber (pattern, index, ch, ret); var ringNumber = ret[0]; this.parseConnection (search, ringNumber, currentAtom, bond); bond = null; } else if (isAtom) { wasMeasure = wasBranch = false; switch (ch) { case '[': case '_': var subPattern = JS.SmilesParser.getSubPattern (pattern, index, ch); index += subPattern.length + (ch == '[' ? 2 : 0); if (this.isBioSequence && ch == '[' && subPattern.indexOf (".") < 0 && subPattern.indexOf ("_") < 0) subPattern += ".0"; currentAtom = this.parseAtom (search, null, subPattern, currentAtom, bond, ch == '[', false, isBranchAtom); currentAtom.hasSubpattern = true; if (bond.order != -1 && bond.order != 0) this.setBondAtom (bond, null, currentAtom, search); bond = null; break; default: var ch2 = (!this.isBioSequence && JU.PT.isUpperCase (ch) ? JS.SmilesParser.getChar (pattern, index + 1) : '\0'); if (ch != 'X' || ch2 != 'x') if (!JU.PT.isLowerCase (ch2) || JU.Elements.elementNumberFromSymbol (pattern.substring (index, index + 2), true) == 0) ch2 = '\0'; if (ch2 != '\0' && "NA CA BA PA SC AC".indexOf (pattern.substring (index, index + 2)) >= 0) { ch2 = '\0'; }var size = (JU.PT.isUpperCase (ch) && JU.PT.isLowerCase (ch2) ? 2 : 1); currentAtom = this.parseAtom (search, null, pattern.substring (index, index + size), currentAtom, bond, false, false, isBranchAtom); bond = null; index += size; } } else { throw new JS.InvalidSmilesException ("Unexpected character: " + JS.SmilesParser.getChar (pattern, index)); }ch = JS.SmilesParser.getChar (pattern, index); if (ch == '}' && this.checkBrace (search, ch, '}')) index++; }pattern = pattern.substring (index); isBranchAtom = false; } }, "JS.SmilesSearch,~S,JS.SmilesAtom,~B"); Clazz.defineMethod (c$, "parseConnection", function (search, ringNum, currentAtom, bond) { var r = Integer.$valueOf (ringNum); var bond0 = this.connections.get (r); if (bond0 == null) { this.connections.put (r, bond); search.top.ringCount++; return; }this.connections.remove (r); switch (bond.order) { case -1: bond.order = (bond0.order != -1 ? bond0.order : this.isSmarts || currentAtom.isAromatic && bond0.atom1.isAromatic ? 81 : 1); break; case 1025: bond.order = 1041; break; case 1041: bond.order = 1025; break; } if (bond0.order != -1 && bond0.order != bond.order || currentAtom === bond0.atom1 || bond0.atom1.getBondTo (currentAtom) != null) throw new JS.InvalidSmilesException ("Bad connection type or atom"); bond0.set (bond); currentAtom.bondCount--; bond0.setAtom2 (currentAtom, search); }, "JS.SmilesSearch,~N,JS.SmilesAtom,JS.SmilesBond"); Clazz.defineMethod (c$, "setBondAtom", function (bond, a1, a2, search) { bond.set2a (a1, a2); if (search != null && bond.order == 2 && bond.atom1 != null && bond.atom2 != null && bond.atom1.isAromatic && bond.atom2.isAromatic && ((this.flags & 512) == 0)) search.setFlags (this.flags = (this.flags | 512)); }, "JS.SmilesBond,JS.SmilesAtom,JS.SmilesAtom,JS.SmilesSearch"); c$.getRingNumber = Clazz.defineMethod (c$, "getRingNumber", function (pattern, index, ch, ret) { var ringNumber; switch (ch) { case '%': if (JS.SmilesParser.getChar (pattern, index + 1) == '(') { var subPattern = JS.SmilesParser.getSubPattern (pattern, index + 1, '('); JS.SmilesParser.getDigits (subPattern, 0, ret); index += subPattern.length + 3; if (ret[0] < 0) throw new JS.InvalidSmilesException ("Invalid number designation: " + subPattern); } else { if (index + 3 <= pattern.length) index = JS.SmilesParser.getDigits (pattern.substring (0, index + 3), index + 1, ret); if (ret[0] < 10) throw new JS.InvalidSmilesException ("Two digits must follow the % sign"); }ringNumber = ret[0]; break; default: ringNumber = ch.charCodeAt (0) - 48; index++; } ret[0] = ringNumber; return index; }, "~S,~N,~S,~A"); Clazz.defineMethod (c$, "checkBioType", function (pattern, index) { this.isBioSequence = (pattern.charAt (index) == '~'); if (this.isBioSequence) { index++; this.bioType = '*'; var ch = JS.SmilesParser.getChar (pattern, 2); if (ch == '~' && ((ch = pattern.charAt (1)) == '*' || JU.PT.isLowerCase (ch))) { this.bioType = ch; index = 3; }}return index; }, "~S,~N"); Clazz.defineMethod (c$, "parseMeasure", function (search, strMeasure, currentAtom) { var pt = strMeasure.indexOf (":"); var id = (pt < 0 ? strMeasure : strMeasure.substring (0, pt)); while (pt != 0) { var len = id.length; if (len == 1) id += "0"; var m = this.htMeasures.get (id); if ((m == null) == (pt < 0) || len == 0) break; try { if (pt > 0) { var type = ("__dat".indexOf (id.charAt (0))); if (type < 2) break; var ret = Clazz.newIntArray (1, 0); JS.SmilesParser.getDigits (id, 1, ret); var index = ret[0]; strMeasure = strMeasure.substring (pt + 1); var isNot = strMeasure.startsWith ("!"); if (isNot) strMeasure = strMeasure.substring (1); var isNegative = (strMeasure.startsWith ("-")); if (isNegative) strMeasure = strMeasure.substring (1); strMeasure = JU.PT.rep (strMeasure, "-", ","); strMeasure = JU.PT.rep (strMeasure, ",,", ",-"); if (isNegative) strMeasure = "-" + strMeasure; var tokens = JU.PT.split (strMeasure, ","); if (tokens.length % 2 == 1 || isNot && tokens.length != 2) break; var vals = Clazz.newFloatArray (tokens.length, 0); var i = tokens.length; for (; --i >= 0; ) if (Float.isNaN (vals[i] = Float.parseFloat (tokens[i]))) break; if (i >= 0) break; m = new JS.SmilesMeasure (search, index, type, isNot, vals); search.measures.addLast (m); if (index > 0) this.htMeasures.put (id, m); else if (index == 0 && JU.Logger.debugging) JU.Logger.debug ("measure created: " + m); } else { if (!m.addPoint (currentAtom.index)) break; if (m.nPoints == m.type) { this.htMeasures.remove (id); if (JU.Logger.debugging) JU.Logger.debug ("measure created: " + m); }return; }if (!m.addPoint (currentAtom.index)) break; } catch (e) { if (Clazz.exceptionOf (e, NumberFormatException)) { break; } else { throw e; } } return; } throw new JS.InvalidSmilesException ("invalid measure: " + strMeasure); }, "JS.SmilesSearch,~S,JS.SmilesAtom"); Clazz.defineMethod (c$, "checkBrace", function (search, ch, type) { switch (ch) { case '{': if (ch != type) break; this.braceCount++; search.top.haveSelected = true; return true; case '}': if (ch != type) break; if (this.braceCount > 0) { this.braceCount--; return true; }break; default: return false; } throw new JS.InvalidSmilesException ("Unmatched '}'"); }, "JS.SmilesSearch,~S,~S"); Clazz.defineMethod (c$, "parseNested", function (search, pattern, prefix) { var index; prefix = "$(" + prefix; while ((index = pattern.lastIndexOf (prefix)) >= 0) { var s = JS.SmilesParser.getSubPattern (pattern, index + 1, '('); var pt = index + s.length + 3; var ext = pattern.substring (pt); pattern = pattern.substring (0, index); var op = ""; if (pattern.endsWith ("]")) throw new JS.InvalidSmilesException ("$(...) must be enclosed in brackets: " + pattern + "$(" + s + ")"); if (index > 1 && prefix.length == 2 && ((pt = pattern.length) > 1 && ",;&![".indexOf (pattern.substring (pt - 1)) < 0)) op = "&"; if (ext.length > 1 && ",;&!)]".indexOf (ext.charAt (0)) < 0) ext = "&" + ext; pattern = pattern + op + "_" + search.top.addNested (s) + "_" + ext; } return pattern; }, "JS.SmilesSearch,~S,~S"); Clazz.defineMethod (c$, "parseVariables", function (pattern) { var keys = new JU.Lst (); var values = new JU.Lst (); var index; var ipt = 0; var iptLast = -1; if (JU.Logger.debugging) JU.Logger.info (pattern); while ((index = pattern.indexOf ("$", ipt)) >= 0) { if (JS.SmilesParser.getChar (pattern, index + 1) == '(') break; ipt = JS.SmilesParser.skipTo (pattern, index, '='); if (ipt <= index + 1 || JS.SmilesParser.getChar (pattern, ipt + 1) != '\"') break; var key = pattern.substring (index, ipt); if (key.lastIndexOf ('$') > 0 || key.indexOf (']') > 0) throw new JS.InvalidSmilesException ("Invalid variable name: " + key); var s = JS.SmilesParser.getSubPattern (pattern, ipt + 1, '\"'); keys.addLast ("[" + key + "]"); values.addLast (s); ipt += s.length + 2; ipt = JS.SmilesParser.skipTo (pattern, ipt, ';'); iptLast = ++ipt; } if (iptLast < 0) return pattern; pattern = pattern.substring (iptLast); for (var i = keys.size (); --i >= 0; ) { var k = keys.get (i); var v = values.get (i); if (!v.equals (k)) pattern = JU.PT.rep (pattern, k, v); } if (JU.Logger.debugging) JU.Logger.info (pattern); return pattern; }, "~S"); Clazz.defineMethod (c$, "parseAtom", function (search, atomSet, pattern, atom, bond, isBracketed, isAND, isBranchAtom) { if (pattern == null || pattern.length == 0) throw new JS.InvalidSmilesException ("Empty atom definition"); var newAtom = new JS.SmilesAtom (); if (this.componentParenCount > 0) newAtom.component = this.componentCount; if (atomSet == null) search.appendAtom (newAtom); var isNewAtom = true; if (!this.checkLogic (search, pattern, newAtom, null, atom, isAND, isBranchAtom, null)) { var ret = Clazz.newIntArray (1, 0); if (this.isBioSequence && pattern.length == 1) pattern += ".0"; var ch = pattern.charAt (0); var index = 0; var isNot = false; if (this.isSmarts && ch == '!') { ch = JS.SmilesParser.getChar (pattern, ++index); if (ch == '\0') throw new JS.InvalidSmilesException ("invalid '!'"); newAtom.not = isNot = true; }var biopt = pattern.indexOf ('.'); if (biopt >= 0) { newAtom.isBioResidue = true; var resOrName = pattern.substring (index, biopt); pattern = pattern.substring (biopt + 1).toUpperCase (); var len = resOrName.length; if ((biopt = resOrName.indexOf ("^")) >= 0) { if (biopt == len - 2) { ch = resOrName.charAt (len - 1); if (ch != '*') newAtom.insCode = ch; }resOrName = resOrName.substring (0, biopt); }if ((biopt = resOrName.indexOf ("#")) >= 0) { JS.SmilesParser.getDigits (resOrName, biopt + 1, ret); newAtom.residueNumber = ret[0]; resOrName = resOrName.substring (0, biopt); }if (resOrName.length == 0) resOrName = "*"; if (resOrName.length > 1) newAtom.residueName = resOrName.toUpperCase (); else if (!resOrName.equals ("*")) newAtom.residueChar = resOrName; resOrName = pattern; if ((biopt = resOrName.indexOf ("#")) >= 0) { JS.SmilesParser.getDigits (resOrName, biopt + 1, ret); newAtom.elementNumber = ret[0]; resOrName = resOrName.substring (0, biopt); }if (resOrName.length == 0) resOrName = "*"; else if (resOrName.equals ("0")) resOrName = "\0"; if (resOrName.equals ("*")) newAtom.isBioAtomWild = true; else newAtom.setAtomName (resOrName); ch = '\0'; }newAtom.setBioAtom (this.bioType); var hydrogenCount = -2147483648; while (ch != '\0' && isNewAtom) { newAtom.setAtomName (this.isBioSequence ? "\0" : ""); if (JU.PT.isDigit (ch)) { index = JS.SmilesParser.getDigits (pattern, index, ret); var mass = ret[0]; if (mass == -2147483648) throw new JS.InvalidSmilesException ("Non numeric atomic mass"); if (JS.SmilesParser.getChar (pattern, index) == '?') { index++; mass = -mass; }if (newAtom.elementDefined) throw new JS.InvalidSmilesException ("atom mass must precede atom symbol or be separated from it with \";\""); newAtom.setAtomicMass (mass); } else { switch (ch) { case '"': var type = JU.PT.getQuotedStringAt (pattern, index); index += type.length + 2; newAtom.atomType = type; break; case '_': index = JS.SmilesParser.getDigits (pattern, index + 1, ret) + 1; if (ret[0] == -2147483648) throw new JS.InvalidSmilesException ("Invalid SEARCH primitive: " + pattern.substring (index)); newAtom.iNested = ret[0]; if (!isBracketed) throw new JS.InvalidSmilesException ("nesting must appear in [...]: $(" + search.getNested (ret[0]) + ")"); if (this.isBioSequence && index != pattern.length) throw new JS.InvalidSmilesException ("invalid characters: " + pattern.substring (index)); break; case '=': index = JS.SmilesParser.getDigits (pattern, index + 1, ret); newAtom.jmolIndex = ret[0]; break; case '#': var isAtomNo = (pattern.charAt (index + 1) == '-'); index = JS.SmilesParser.getDigits (pattern, index + (isAtomNo ? 2 : 1), ret); if (isAtomNo) newAtom.atomNumber = ret[0]; else newAtom.elementNumber = ret[0]; break; case '-': case '+': index = this.checkCharge (pattern, index, newAtom); break; case '@': if (search.stereo == null) search.stereo = JS.SmilesStereo.newStereo (search); index = JS.SmilesStereo.checkChirality (search, pattern, index, search.patternAtoms[newAtom.index]); break; case ':': index = JS.SmilesParser.getDigits (pattern, ++index, ret); if (ret[0] == -2147483648) throw new JS.InvalidSmilesException ("Invalid atom class"); newAtom.atomClass = ret[0]; break; default: var nextChar = JS.SmilesParser.getChar (pattern, index + 1); var len = index + (JU.PT.isLowerCase (nextChar) && (!isBracketed || !JU.PT.isDigit (JS.SmilesParser.getChar (pattern, index + 2))) ? 2 : 1); var sym2 = pattern.substring (index + 1, len); var symbol = Character.toUpperCase (ch) + sym2; var mustBeSymbol = true; var checkForPrimitive = (isBracketed && JU.PT.isLetter (ch)); if (checkForPrimitive) { if (!isNot && (isAND ? atomSet : newAtom).hasSymbol) { mustBeSymbol = false; } else if (ch == 'H') { mustBeSymbol = (pattern.length == 1 || !JU.PT.isDigit (nextChar)); } else if (JU.PT.isDigit (nextChar)) { mustBeSymbol = false; } else if (!symbol.equals ("A") && !symbol.equals ("Xx")) { mustBeSymbol = ((ch == 'h' ? len == 2 : true) && JU.Elements.elementNumberFromSymbol (symbol, true) > 0); if (!mustBeSymbol && len == 2) { sym2 = ""; symbol = symbol.substring (0, 1); mustBeSymbol = (JU.Elements.elementNumberFromSymbol (symbol, true) > 0); }}}if (mustBeSymbol) { if (!isBracketed && !this.isSmarts && !this.isBioSequence && !JS.SmilesAtom.allowSmilesUnbracketed (symbol) || !newAtom.setSymbol (symbol = ch + sym2)) throw new JS.InvalidSmilesException ("Invalid atom symbol: " + symbol); if (isAND) atomSet.hasSymbol = true; index += symbol.length; } else { index = JS.SmilesParser.getDigits (pattern, index + 1, ret); var val = ret[0]; switch (ch) { default: throw new JS.InvalidSmilesException ("Invalid SEARCH primitive: " + pattern.substring (index)); case 'D': newAtom.setDegree (val == -2147483648 ? 1 : val); break; case 'd': newAtom.setNonhydrogenDegree (val == -2147483648 ? 1 : val); break; case 'H': hydrogenCount = (val == -2147483648 ? 1 : val); break; case 'h': newAtom.setImplicitHydrogenCount (val == -2147483648 ? -1 : val); break; case 'R': if (val == -2147483648) val = -1; newAtom.setRingMembership (val); search.top.needRingData = true; break; case 'r': if (val == -2147483648) { val = -1; newAtom.setRingMembership (val); } else { newAtom.setRingSize (val); switch (val) { case 500: val = 5; break; case 600: val = 6; break; } if (val > search.ringDataMax) search.ringDataMax = val; }search.top.needRingData = true; break; case 'v': newAtom.setValence (val == -2147483648 ? 1 : val); break; case 'X': newAtom.setConnectivity (val == -2147483648 ? 1 : val); break; case 'x': newAtom.setRingConnectivity (val == -2147483648 ? -1 : val); search.top.needRingData = true; break; } }} }ch = JS.SmilesParser.getChar (pattern, index); if (isNot && ch != '\0') throw new JS.InvalidSmilesException ("'!' may only involve one primitive."); } if (hydrogenCount == -2147483648 && isBracketed) hydrogenCount = -2147483647; newAtom.setExplicitHydrogenCount (hydrogenCount); search.patternAtoms[newAtom.index].setExplicitHydrogenCount (hydrogenCount); }if (this.braceCount > 0) newAtom.selected = true; if (isNewAtom && atomSet != null) atomSet.addSubAtom (newAtom, isAND); if (atom != null && bond.order == 0) { newAtom.notBondedIndex = atom.index; }if (atom != null && bond.order != 0) { if (bond.order == -1) bond.order = (this.isBioSequence && isBranchAtom ? 112 : this.isSmarts || atom.isAromatic && newAtom.isAromatic ? 81 : 1); if (!isBracketed) this.setBondAtom (bond, null, newAtom, search); if (this.branchLevel == 0 && (bond.order == 17 || bond.order == 112)) this.branchLevel++; }if (this.branchLevel == 0) search.lastChainAtom = newAtom; return newAtom; }, "JS.SmilesSearch,JS.SmilesAtom,~S,JS.SmilesAtom,JS.SmilesBond,~B,~B,~B"); Clazz.defineMethod (c$, "checkCharge", function (pattern, index, newAtom) { var len = pattern.length; var ch = pattern.charAt (index); var count = 1; ++index; if (index < len) { var nextChar = pattern.charAt (index); if (JU.PT.isDigit (nextChar)) { var ret = Clazz.newIntArray (1, 0); index = JS.SmilesParser.getDigits (pattern, index, ret); count = ret[0]; if (count == -2147483648) throw new JS.InvalidSmilesException ("Non numeric charge"); } else { while (index < len && pattern.charAt (index) == ch) { index++; count++; } }}newAtom.setCharge (ch == '+' ? count : -count); return index; }, "~S,~N,JS.SmilesAtom"); Clazz.defineMethod (c$, "parseBond", function (search, bondSet, pattern, bond, currentAtom, isAND, isBranchAtom, len, ret) { var ch; if (len > 0) { switch (ch = pattern.charAt (0)) { case '>': if (!pattern.equals (">>")) { len = -1; break; }case '.': if (bond == null && bondSet == null) { this.isBioSequence = (JS.SmilesParser.getChar (pattern, 1) == '~'); return new JS.SmilesBond (null, null, 0, false); }len = -1; break; case '+': if (bondSet != null) len = -1; break; } } else { ch = '\0'; }var newBond = (bondSet == null ? (bond == null ? new JS.SmilesBond (currentAtom, null, (this.isBioSequence && currentAtom != null ? (isBranchAtom ? 112 : 96) : -1), false) : bond) : isAND ? bondSet.addPrimitive () : bondSet.addBondOr ()); if (len > 0 && !this.checkLogic (search, pattern, null, newBond, currentAtom, isAND, false, ret)) { var isBondNot = (ch == '!'); if (isBondNot) { ch = JS.SmilesParser.getChar (pattern, 1); if (ch == '\0' || ch == '!') throw new JS.InvalidSmilesException ("invalid '!'"); }var bondType = JS.SmilesBond.getBondTypeFromCode (ch); if (bondType == 65) search.top.needRingMemberships = true; if (currentAtom == null && bondType != 0) throw new JS.InvalidSmilesException ("Bond without a previous atom"); switch (bondType) { case 65537: case 65538: if ((len = pattern.length) < (isBondNot ? 3 : 2) || pattern.charAt (len - 1) != '-') { len = 0; } else { if (len == (isBondNot ? 3 : 2)) { newBond.setAtropType (22); } else { JS.SmilesParser.getDigits (pattern, (isBondNot ? 2 : 1), ret); newBond.setAtropType (ret[0]); }}search.haveBondStereochemistry = true; break; case 1025: case 1041: this.bondDirectionPaired = !this.bondDirectionPaired; search.haveBondStereochemistry = true; break; case 17: break; case 2: search.top.nDouble++; case 1: if (currentAtom.isAromatic) search.top.needRingData = true; break; } newBond.set2 (bondType, isBondNot); if (this.isBioSequence && bondSet != null) bondSet.set2 (bondType, isBondNot); }if (len == -1) throw new JS.InvalidSmilesException ("invalid bond:" + ch); return newBond; }, "JS.SmilesSearch,JS.SmilesBond,~S,JS.SmilesBond,JS.SmilesAtom,~B,~B,~N,~A"); Clazz.defineMethod (c$, "checkLogic", function (search, pattern, atom, bond, currentAtom, isAND, isBranchAtom, ret) { var pt = pattern.lastIndexOf ("!"); if (atom != null) atom.pattern = pattern; while (pt > 0) { if (",;&!".indexOf (pattern.charAt (pt - 1)) < 0) pattern = pattern.substring (0, pt) + "&" + pattern.substring (pt); pt = pattern.lastIndexOf ("!", pt - 1); } pt = pattern.indexOf (','); var len = pattern.length; var and = "&"; out : while (true) { var haveOr = (pt > 0); if (haveOr && !this.isSmarts || pt == 0) break; pt = pattern.indexOf (';'); if (pt >= 0) { if (!this.isSmarts || pt == 0) break; if (haveOr) { and = ";"; haveOr = false; } else { pattern = pattern.$replace (';', '&'); }}var index = 0; if (haveOr) { pattern += ","; while ((pt = pattern.indexOf (',', index)) > 0 && pt <= len) { var s = pattern.substring (index, pt); if (s.length == 0) throw new JS.InvalidSmilesException ("missing " + (bond == null ? "atom" : "bond") + " token"); if (bond == null) this.parseAtom (search, atom, s, null, null, true, false, isBranchAtom); else this.parseBond (search, bond, s, null, currentAtom, false, false, s.length, ret); index = pt + 1; } } else if ((pt = pattern.indexOf (and)) >= 0 || bond != null && len > 1 && !isAND) { if (pt == 0 || bond == null && !this.isSmarts) break; if (bond != null && pt < 0) { if (len > 1) { var sNew = new JU.SB (); for (var i = 0; i < len; ) { var ch = pattern.charAt (i++); sNew.appendC (ch); switch (ch) { case '!': if (!this.isSmarts) break out; continue; case '^': case '`': while ((ch = pattern.charAt (i++)) != '-' && ch != '\0') { sNew.appendC (ch); } sNew.appendC ('-'); break; } if (i < len) { if (!this.isSmarts) break out; sNew.append (and); }} pattern = sNew.toString (); len = pattern.length; }}pattern += and; while ((pt = pattern.indexOf (and, index)) > 0 && pt <= len) { var s = pattern.substring (index, pt); if (bond == null) this.parseAtom (search, atom, s, null, null, true, true, isBranchAtom); else this.parseBond (search, this.isSmarts ? bond : null, s, this.isSmarts ? null : bond, currentAtom, true, false, s.length, ret); index = pt + 1; } } else { return false; }return true; } var ch = pattern.charAt (pt); throw new JS.InvalidSmilesException ((this.isSmarts ? "invalid placement for '" + ch + "'" : "[" + ch + "] notation only valid with SMARTS, not SMILES,") + " in " + pattern); }, "JS.SmilesSearch,~S,JS.SmilesAtom,JS.SmilesBond,JS.SmilesAtom,~B,~B,~A"); c$.getSubPattern = Clazz.defineMethod (c$, "getSubPattern", function (pattern, index, ch) { var ch2; var margin = 1; switch (ch) { case '[': ch2 = ']'; break; case '"': case '%': case '/': ch2 = ch; break; case '(': ch2 = ')'; break; default: ch2 = ch; margin = 0; } var len = pattern.length; var pCount = 1; for (var pt = index + 1; pt < len; pt++) { var ch1 = pattern.charAt (pt); if (ch1 == ch2) { pCount--; if (pCount == 0) return pattern.substring (index + margin, pt + 1 - margin); } else if (ch1 == ch) { pCount++; }} throw new JS.InvalidSmilesException ("Unmatched " + ch); }, "~S,~N,~S"); c$.getChar = Clazz.defineMethod (c$, "getChar", function (pattern, i) { return (i < pattern.length ? pattern.charAt (i) : '\0'); }, "~S,~N"); c$.getDigits = Clazz.defineMethod (c$, "getDigits", function (pattern, index, ret) { var pt = index; var len = pattern.length; while (pt < len && JU.PT.isDigit (pattern.charAt (pt))) pt++; if (pt > index) try { ret[0] = Integer.parseInt (pattern.substring (index, pt)); return pt; } catch (e) { if (Clazz.exceptionOf (e, NumberFormatException)) { } else { throw e; } } ret[0] = -2147483648; return pt; }, "~S,~N,~A"); c$.skipTo = Clazz.defineMethod (c$, "skipTo", function (pattern, index, ch0) { var pt = index; var ch; while ((ch = JS.SmilesParser.getChar (pattern, ++pt)) != ch0 && ch != '\0') { } return (ch == '\0' ? -1 : pt); }, "~S,~N,~S"); c$.cleanPattern = Clazz.defineMethod (c$, "cleanPattern", function (pattern) { pattern = JU.PT.replaceAllCharacters (pattern, " \t\n\r", ""); pattern = JU.PT.rep (pattern, "^^", "`"); var i = 0; var i2 = 0; while ((i = pattern.indexOf ("//*")) >= 0 && (i2 = pattern.indexOf ("*//")) >= i) pattern = pattern.substring (0, i) + pattern.substring (i2 + 3); pattern = JU.PT.rep (pattern, "//", ""); return pattern; }, "~S"); c$.extractFlags = Clazz.defineMethod (c$, "extractFlags", function (pattern, ret) { pattern = JS.SmilesParser.cleanPattern (pattern); var flags = 0; while (pattern.startsWith ("/")) { var strFlags = JS.SmilesParser.getSubPattern (pattern, 0, '/').toUpperCase (); pattern = pattern.substring (strFlags.length + 2); flags = JS.SmilesSearch.addFlags (flags, strFlags); } ret[0] = flags; return pattern; }, "~S,~A"); c$.getFlags = Clazz.defineMethod (c$, "getFlags", function (pattern) { var ret = Clazz.newIntArray (1, 0); JS.SmilesParser.extractFlags (pattern, ret); return ret[0]; }, "~S"); });