1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2006-10-20 07:48:25 -0500 (Fri, 20 Oct 2006) $
4  * $Revision: 5991 $
5  *
6  * Copyright (C) 2003-2005  Miguel, Jmol Development, www.jmol.org
7  *
8  * Contact: jmol-developers@lists.sf.net
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 package org.jmol.adapter.readers.cif;
25 
26 import java.util.Hashtable;
27 import java.util.Map;
28 
29 import javajs.util.Lst;
30 import javajs.util.M4;
31 import javajs.util.P3;
32 import javajs.util.PT;
33 import javajs.util.SB;
34 
35 import org.jmol.adapter.smarter.Atom;
36 import org.jmol.adapter.smarter.Structure;
37 import org.jmol.api.JmolAdapter;
38 import org.jmol.c.STR;
39 import javajs.util.BS;
40 import org.jmol.util.BSUtil;
41 import org.jmol.util.Logger;
42 import org.jmol.util.SimpleUnitCell;
43 
44 
45 /**
46  *
47  * mmCIF files are recognized prior to class creation.
48  * Required fields include one of:
49  *
50  *   _entry.id
51  *   _database_PDB_
52  *   _pdbx_
53  *   _chem_comp.pdbx_type
54  *   _audit_author.name
55  *   _atom_site.
56  *
57  *
58  * @author Bob Hanson (hansonr@stolaf.edu)
59  *
60  */
61 public class MMCifReader extends CifReader {
62 
63   protected boolean isBiomolecule;
64   private boolean byChain, bySymop;
65   private Map<String, P3> chainAtomMap;
66   private Map<String, int[]> chainAtomCounts;
67 
68   protected Lst<Map<String, Object>> vBiomolecules;
69   private Map<String, M4> htBiomts;
70   protected Map<String, Map<String, Object>> htSites;
71   protected Map<String, String> htHetero;
72   private Map<String, Lst<Object[]>> htBondMap;
73   private Map<String, BS> assemblyIdAtoms;
74 
75   private int thisChain = -1;
76   private int modelIndex = 0;
77 
78   private P3 chainSum;
79   private int[] chainAtomCount;
80 
81   private boolean isLigandBondBug;
82   // Jmol-14.3.3_2014.07.27 broke mmCIF bond reading for ligands
83   // Jmol-14.3.9_2014.11.11 fixes this.
84 
85   M4 mident;
86 
87   @Override
initSubclass()88   protected void initSubclass() {
89     setIsPDB();
90     mident = M4.newM4(null);
91     isMMCIF = true;
92     if (isDSSP1)
93       asc.setInfo("isDSSP1",Boolean.TRUE);
94     if (htParams.containsKey("isMutate"))
95       asc.setInfo("isMutate",Boolean.TRUE);
96     doSetBonds = checkFilterKey("ADDBONDS");
97     byChain = checkFilterKey("BYCHAIN");
98     if (checkFilterKey("BIOMOLECULE")) // PDB format
99       filter = PT.rep(filter, "BIOMOLECULE", "ASSEMBLY");
100     isBiomolecule = checkFilterKey("ASSEMBLY");
101     if (isBiomolecule) {
102       filter = filter.replace(':', ' '); // no chain selection for biomolecules
103       bySymop = checkFilterKey("BYSYMOP");
104     }
105     isCourseGrained = byChain || bySymop;
106     if (isCourseGrained) {
107       chainAtomMap = new Hashtable<String, P3>();
108       chainAtomCounts = new Hashtable<String, int[]>();
109     }
110     // When this reader was split off from CifReader, a bug was introduced
111     // into the Resolver that made it so that ligand files were read by
112     // CifReader and not MMCifReader. This caused CHEM_COMP_BOND records to be
113     // skipped and so in the case of pdbAddHydrogen no hydrogens added.
114     isLigandBondBug = (stateScriptVersionInt >= 140204 && stateScriptVersionInt <= 140208
115         || stateScriptVersionInt >= 140304 && stateScriptVersionInt <= 140308);
116 
117   }
118 
119   @Override
processSubclassEntry()120   protected void processSubclassEntry() throws Exception {
121     if (key0.startsWith(FAMILY_ASSEM_CAT)
122         || key0.startsWith(FAMILY_STRUCTCONN_CAT)
123         || key0.startsWith(FAMILY_SEQUENCEDIF_CAT)
124         || key0.startsWith(FAMILY_STRUCTCONF_CAT)
125         || key0.startsWith(FAMILY_SHEET_CAT)
126 
127 //        || key0.startsWith(FAMILY_PDBX_NONPOLY_CAT)
128         )
129       processSubclassLoopBlock();
130     else if (key.equals("_rna3d")) {
131       addedData = data;
132       addedDataKey = key;
133     } else if (key.equals("_dssr")) {
134       dssr = vwr.parseJSONMap(reader.readLine());
135       reader.readLine(); // sometimes there is a null character here
136     }
137   }
138 
139   @Override
processSubclassLoopBlock()140   protected boolean processSubclassLoopBlock() throws Exception {
141     if (key0.startsWith(FAMILY_NCS_CAT))
142       return processStructOperListBlock(true);
143     if (key0.startsWith(FAMILY_OPER_CAT))
144       return processStructOperListBlock(false);
145     if (key0.startsWith(FAMILY_ASSEM_CAT))
146       return processAssemblyGenBlock();
147     if (key0.startsWith(FAMILY_SEQUENCEDIF_CAT))
148       return processSequence();
149 
150     if (isCourseGrained)
151       return false;
152 
153     if (key0.startsWith(FAMILY_STRUCSITE_CAT))
154       return processStructSiteBlock();
155     if (key0.startsWith(FAMILY_CHEMCOMP_CAT))
156       return processChemCompLoopBlock();
157 //    if (key0.startsWith(FAMILY_PDBX_NONPOLY_CAT))
158 //      return processNonpolyLoopBlock();
159     if (key0.startsWith(FAMILY_STRUCTCONF_CAT))
160       return processStructConfLoopBlock();
161     if (key0.startsWith(FAMILY_SHEET_CAT))
162       return processStructSheetRangeLoopBlock();
163 
164     // alas -- saved states must not read ligand bonding
165     // the problem was that these files were not recognized as mmCIF
166     // files by the resolver when this MMCifReader was created.
167 
168     if (isLigandBondBug)
169       return false;
170     if (key0.startsWith(FAMILY_COMPBOND_CAT))
171       return processCompBondLoopBlock();
172     if (key0.startsWith(FAMILY_STRUCTCONN_CAT))
173       return processStructConnLoopBlock();
174 
175     return false;
176 
177   }
178 
179   private boolean requiresSorting;
180 
181   /**
182    * issue here is that mmCIF assembly atoms can be in different blocks by chain:
183    * Model1:Chain1 Model2:Chain1 Model1:Chain2 Model2:Chain2 ... and so assigned
184    * to too many atom sets.
185    *
186    */
sortAssemblyModels()187   protected void sortAssemblyModels() {
188     int natoms = asc.ac;
189     int lastSet = -1;
190     Atom[] atoms = asc.atoms;
191     Atom[] newAtoms = new Atom[natoms];
192     String[] ids = PT.split("," + modelStrings + ",", ",,");
193     BS bsAtomsNew = (asc.bsAtoms == null ? null : BS.newN(asc.bsAtoms.size()));
194     for (int im = 1, n = 0; im < ids.length; im++) {
195       String sModel = ids[im];
196       int modelIndex = -1;
197       for (int is = 0; is < asc.atomSetCount; is++) {
198         int ia0 = asc.getAtomSetAtomIndex(is);
199         int ia1 = ia0 + asc.getAtomSetAtomCount(is);
200         String am = "" + modelMap.get("_" + is);
201         if (am.equals(sModel)) {
202           if (modelIndex < 0 && (modelIndex = is) > lastSet)
203             lastSet = is;
204           for (int i = ia0; i < ia1; i++) {
205             if (bsAtomsNew == null || asc.bsAtoms.get(i)) {
206               if (bsAtomsNew != null)
207                 bsAtomsNew.set(n);
208               atoms[i].atomSetIndex = modelIndex;
209               newAtoms[n++] = atoms[i];
210             }
211           }
212         }
213       }
214 
215     }
216     asc.atoms = newAtoms;
217     asc.bsAtoms = bsAtomsNew;
218     if (++lastSet < asc.atomSetCount)
219       asc.atomSetCount = lastSet;
220   }
221 
222 
223   @Override
finalizeSubclass()224   protected boolean finalizeSubclass() throws Exception {
225     if (byChain && !isBiomolecule)
226       for (String id : chainAtomMap.keySet())
227         createParticle(id);
228     boolean haveBiomolecule = (isBiomolecule && vBiomolecules != null && vBiomolecules.size() > 0);
229     if (!isCourseGrained && asc.ac == nAtoms) {
230       asc.removeCurrentAtomSet();
231     } else {
232       if ((dssr != null || validation != null || addedData != null) && !isCourseGrained && !requiresSorting) {
233         MMCifValidationParser vs = ((MMCifValidationParser) getInterface("org.jmol.adapter.readers.cif.MMCifValidationParser"))
234             .set(this);
235         String note = null;
236         if (addedData == null) {
237           if (validation != null || dssr != null)
238             note = vs.finalizeValidations(vwr, modelMap);
239         } else if (addedDataKey.equals("_rna3d")) {
240           note = vs.finalizeRna3d(modelMap);
241         }
242         if (note != null)
243           appendLoadNote(note);
244       }
245       setHetero();
246       if (doSetBonds)
247         setBonds();
248     }
249     if (asc.ac == 0 && !isCourseGrained)
250       return false;
251     String spaceGroup = sgName;
252     if (htSites != null)
253       addSites(htSites);
254 
255     if (haveBiomolecule) {
256       asc.setCurrentModelInfo("biomolecules", vBiomolecules);
257       setBiomolecules();
258       if (thisBiomolecule != null) {
259         if (iHaveFractionalCoordinates)
260           fractionalizeCoordinates(false);
261         asc.getXSymmetry().applySymmetryBio(thisBiomolecule,
262             applySymmetryToBonds, filter);
263         asc.xtalSymmetry = null;
264       }
265       doCheckUnitCell &= iHaveUnitCell && doApplySymmetry;
266       if (doCheckUnitCell) {
267         ignoreFileSpaceGroupName = true;
268         sgName = spaceGroup;
269         fractionalizeCoordinates(true);
270         asc.setCurrentModelInfo("biosymmetry", null);
271         asc.setCurrentModelInfo("biosymmetryCount", null);
272         asc.checkSpecial = false;
273         if (byChain)
274           return true;
275       }
276     }
277     if (latticeCells != null && latticeCells[0] != 0)
278       addJmolScript("unitcell;axes on;axes unitcell;");
279     if (requiresSorting)
280       sortAssemblyModels();
281     return true;
282   }
283 
284   ////////////////////////////////////////////////////////////////
285   // assembly data
286   ////////////////////////////////////////////////////////////////
287 
288   @Override
checkSubclassSymmetry()289   protected boolean checkSubclassSymmetry() {
290   asc.checkSpecial = false;
291   int modelIndex = asc.iSet;
292   asc.setCurrentModelInfo(
293       "PDB_CONECT_firstAtom_count_max",
294       new int[] { asc.getAtomSetAtomIndex(modelIndex),
295           asc.getAtomSetAtomCount(modelIndex), maxSerial });
296     return false;
297   }
298 
299   /**
300    * Note that setting bonds from _struct_conn is only done if we have updated
301    * CIF files, which include _chem_comp_bond.
302    */
setBonds()303   private void setBonds() {
304     if (htBondMap == null)
305       return;
306     BS bsAtoms = asc.bsAtoms;
307     if (bsAtoms == null)
308       bsAtoms = BSUtil.newBitSet2(0, asc.ac);
309     Atom[] atoms = asc.atoms;
310     float seqid = -1;
311     String comp = null;
312     Map<Object, Integer> map = null;
313     for (int i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms.nextSetBit(i + 1)) {
314       Atom a = atoms[i];
315       float pt = (a.vib == null ? a.sequenceNumber : a.vib.x);
316       if (pt != seqid) {
317         seqid = pt;
318         if (comp != null)
319           processBonds(htBondMap.get(comp), map, false);
320         map = new Hashtable<Object, Integer>();
321         comp = atoms[i].group3;
322         if (!htBondMap.containsKey(comp)) {
323           comp = null;
324           continue;
325         }
326       }
327       if (comp == null)
328         continue;
329       map.put(a.atomName, Integer.valueOf(a.index));
330     }
331     if (comp != null)
332       processBonds(htBondMap.get(comp), map, false);
333     if (structConnMap != null) {
334       map = new Hashtable<Object, Integer>();
335       seqid = -1;
336       comp = null;
337       for (int i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms.nextSetBit(i + 1)) {
338         Atom a = atoms[i];
339         float pt = (a.vib == null ? a.sequenceNumber : a.vib.x);
340         if (pt != seqid) {
341           seqid = pt;
342           String ckey = a.chainID + a.group3 + seqid;
343           if (structConnList.indexOf(ckey) < 0) {
344             comp = null;
345             continue;
346           }
347           comp = ckey;
348         }
349         if (comp == null)
350           continue;
351         map.put(comp + a.atomName + a.altLoc, Integer.valueOf(a.index));
352       }
353       processBonds(structConnMap, map, true);
354     }
355     appendLoadNote(asc.bondCount + " bonds added");
356   }
357 
processBonds(Lst<Object[]> cmap, Map<Object, Integer> map, boolean isStructConn)358   private void processBonds(Lst<Object[]> cmap, Map<Object, Integer> map, boolean isStructConn) {
359     Integer i1, i2;
360     for (int i = 0, n = cmap.size(); i < n; i++) {
361       Object[] o = cmap.get(i);
362       if ((i1 = map.get(o[0])) == null || (i2 = map.get(o[1])) == null)
363         continue;
364       if (debugging)
365         Logger.debug((isStructConn ? "_struct_conn" : "_comp_bond") + " adding bond " + i1 + " " + i2 + " order=" + o[2]);
366       asc.addNewBondWithOrder(i1.intValue(), i2.intValue(), ((Integer) o[2]).intValue());
367     }
368   }
369 
370   final private static byte OPER_ID = 12;
371   final private static byte OPER_XYZ = 13;
372 
373   final private static String FAMILY_NCS_CAT = "_struct_ncs_oper.";
374   final private static String FAMILY_NCS = "_struct_ncs_oper";
375   final private static String[] ncsoperFields = {
376     "*_matrix[1][1]",
377     "*_matrix[1][2]",
378     "*_matrix[1][3]",
379     "*_vector[1]",
380     "*_matrix[2][1]",
381     "*_matrix[2][2]",
382     "*_matrix[2][3]",
383     "*_vector[2]",
384     "*_matrix[3][1]",
385     "*_matrix[3][2]",
386     "*_matrix[3][3]",
387     "*_vector[3]",
388     "*_id",
389     "*_symmetry_operation"
390   };
391 
392   final private static String FAMILY_OPER_CAT = "_pdbx_struct_oper_list.";
393   final private static String FAMILY_OPER = "_pdbx_struct_oper_list";
394   final private static String[] operFields = {
395     "*_matrix[1][1]",
396     "*_matrix[1][2]",
397     "*_matrix[1][3]",
398     "*_vector[1]",
399     "*_matrix[2][1]",
400     "*_matrix[2][2]",
401     "*_matrix[2][3]",
402     "*_vector[2]",
403     "*_matrix[3][1]",
404     "*_matrix[3][2]",
405     "*_matrix[3][3]",
406     "*_vector[3]",
407     "*_id",
408     "*_symmetry_operation"
409   };
410 
411   final private static byte ASSEM_ID = 0;
412   final private static byte ASSEM_OPERS = 1;
413   final private static byte ASSEM_LIST = 2;
414 
415   final private static String FAMILY_ASSEM_CAT = "_pdbx_struct_assembly_gen.";
416 
417   final private static String[] assemblyFields = {
418     "_pdbx_struct_assembly_gen_assembly_id",
419     "_pdbx_struct_assembly_gen_oper_expression",
420     "_pdbx_struct_assembly_gen_asym_id_list"
421   };
422 
423   /*
424   _pdbx_struct_assembly_gen.assembly_id       1
425   _pdbx_struct_assembly_gen.oper_expression   1,2,3,4
426   _pdbx_struct_assembly_gen.asym_id_list      A,B,C
427   #
428   loop_
429   _pdbx_struct_oper_list.id
430   _pdbx_struct_oper_list.type
431   _pdbx_struct_oper_list.name
432   _pdbx_struct_oper_list.symmetry_operation
433   _pdbx_struct_oper_list.matrix[1][1]
434   _pdbx_struct_oper_list.matrix[1][2]
435   _pdbx_struct_oper_list.matrix[1][3]
436   _pdbx_struct_oper_list.vector[1]
437   _pdbx_struct_oper_list.matrix[2][1]
438   _pdbx_struct_oper_list.matrix[2][2]
439   _pdbx_struct_oper_list.matrix[2][3]
440   _pdbx_struct_oper_list.vector[2]
441   _pdbx_struct_oper_list.matrix[3][1]
442   _pdbx_struct_oper_list.matrix[3][2]
443   _pdbx_struct_oper_list.matrix[3][3]
444   _pdbx_struct_oper_list.vector[3]
445   1 'identity operation'         1_555  x,y,z          1.0000000000  0.0000000000  0.0000000000 0.0000000000  0.0000000000
446   1.0000000000  0.0000000000 0.0000000000  0.0000000000 0.0000000000 1.0000000000  0.0000000000
447   2 'crystal symmetry operation' 15_556 y,x,-z+1       0.0000000000  1.0000000000  0.0000000000 0.0000000000  1.0000000000
448   0.0000000000  0.0000000000 0.0000000000  0.0000000000 0.0000000000 -1.0000000000 52.5900000000
449   3 'crystal symmetry operation' 10_665 -x+1,-y+1,z    -1.0000000000 0.0000000000  0.0000000000 68.7500000000 0.0000000000
450   -1.0000000000 0.0000000000 68.7500000000 0.0000000000 0.0000000000 1.0000000000  0.0000000000
451   4 'crystal symmetry operation' 8_666  -y+1,-x+1,-z+1 0.0000000000  -1.0000000000 0.0000000000 68.7500000000 -1.0000000000
452   0.0000000000  0.0000000000 68.7500000000 0.0000000000 0.0000000000 -1.0000000000 52.5900000000
453   #
454 
455    */
456 
457   final private static String FAMILY_SEQUENCEDIF_CAT = "_struct_ref_seq_dif.";
458   final private static byte STRUCT_REF_G3 = 0;
459   final private static byte STRUCT_REF_G1 = 1;
460   final private static String[] structRefFields = {
461     "_struct_ref_seq_dif_mon_id",
462     "_struct_ref_seq_dif_db_mon_id"
463   };
464 
465   /**
466    * get canonical 1-letter DNA/RNA sequence code from 3-letter code. For example, "2MG" --> "G"
467    * @return true
468    * @throws Exception
469    */
processSequence()470   private boolean processSequence() throws Exception {
471     parseLoopParameters(structRefFields);
472     String g1, g3;
473     while (cifParser.getData()) {
474       if (isNull(g1 = getField(STRUCT_REF_G1).toLowerCase())
475           || g1.length() != 1 || isNull(g3 = getField(STRUCT_REF_G3)))
476         continue;
477       if (htGroup1 == null)
478         asc.setInfo("htGroup1", htGroup1 = new Hashtable<String, String>());
479       htGroup1.put(g3, g1);
480     }
481     return true;
482   }
483 
processAssemblyGenBlock()484   private boolean processAssemblyGenBlock() throws Exception {
485     parseLoopParameters(assemblyFields);
486     while (cifParser.getData()) {
487       String[] assem = new String[3];
488       int count = 0;
489       int p;
490       int n = cifParser.getColumnCount();
491       for (int i = 0; i < n; ++i) {
492         switch (p = fieldProperty(i)) {
493         case ASSEM_ID:
494         case ASSEM_OPERS:
495         case ASSEM_LIST:
496           count++;
497           assem[p] = field;
498           break;
499         }
500       }
501       if (count == 3)
502         addAssembly(assem);
503     }
504     return true;
505   }
506 
507   @SuppressWarnings("unchecked")
addAssembly(String[] assem)508   protected void addAssembly(String[] assem) throws Exception {
509     String id = assem[ASSEM_ID];
510     String list = assem[ASSEM_LIST];
511     String operators = assem[ASSEM_OPERS];
512     String name = "biomolecule " + id;
513     Logger.info(name + " operators " + operators
514         + " ASYM_IDs " + list);
515     appendLoadNote("found " + name + ": " + list);
516     if (vBiomolecules == null)
517       vBiomolecules = new Lst<Map<String, Object>>();
518     Map<String, Object> info = null;
519     for (int i = vBiomolecules.size(); --i >= 0;)
520       if (vBiomolecules.get(i).get("name").equals(name)) {
521         info = vBiomolecules.get(i);
522         break;
523       }
524     if (info == null) {
525       info = new Hashtable<String, Object>();
526       info.put("name", name);
527       int iMolecule = parseIntStr(id);
528       info.put("molecule",
529         iMolecule == Integer.MIN_VALUE ? id : Integer.valueOf(iMolecule));
530       info.put("biomts", new Lst<M4>());
531       info.put("chains", new Lst<String>());
532       info.put("assemblies", new Lst<String>());
533       info.put("operators", new Lst<String>());
534       vBiomolecules.addLast(info);
535     }
536     ((Lst<String>) info.get("assemblies")).addLast("$" + list.replace(',', '$'));
537     ((Lst<String>) info.get("operators")).addLast(decodeAssemblyOperators(operators));
538     checkFilterAssembly(id, info);
539   }
540 
checkFilterAssembly(String id, Map<String, Object> info)541   protected void checkFilterAssembly(String id, Map<String, Object> info) {
542     if (checkFilterKey("ASSEMBLY " + id + ";") || checkFilterKey("ASSEMBLY=" + id + ";"))
543       thisBiomolecule = info;
544   }
545 
decodeAssemblyOperators(String ops)546   private String decodeAssemblyOperators(String ops) {
547 
548     //    Identifies the operation of collection of operations
549     //    from category PDBX_STRUCT_OPER_LIST.
550     //
551     //    Operation expressions may have the forms:
552     //
553     //     (1)        the single operation 1
554     //     (1,2,5)    the operations 1, 2, 5
555     //     (1-4)      the operations 1,2,3 and 4
556     //     (1,2)(3,4) the combinations of operations
557     //                3 and 4 followed by 1 and 2 (i.e.
558     //                the cartesian product of parenthetical
559     //                groups applied from right to left)
560     int pt = ops.indexOf(")(");
561     if (pt >= 0)
562       return crossBinary(decodeAssemblyOperators(ops.substring(0, pt + 1)),
563           decodeAssemblyOperators(ops.substring(pt + 1)));
564     if (ops.startsWith("(")) {
565       if (ops.indexOf("-") >= 0)
566         ops = BS.unescape(
567             "({" + ops.substring(1, ops.length() - 1).replace('-', ':').replace(',', ' ') + "})")
568             .toJSON();
569       ops = PT.rep(ops, " ", "");
570       ops = ops.substring(1, ops.length() - 1);
571     }
572     return ops;
573   }
574 
crossBinary(String ops1, String ops2)575   private String crossBinary(String ops1, String ops2) {
576     SB sb = new SB();
577     String[] opsLeft = PT.split(ops1, ",");
578     String[] opsRight = PT.split(ops2, ",");
579     for (int i = 0; i < opsLeft.length; i++)
580       for (int j = 0; j < opsRight.length; j++)
581         sb.append(",").append(opsLeft[i]).append("|").append(opsRight[j]);
582     return sb.toString().substring(1);
583   }
584 
processStructOperListBlock(boolean isNCS)585   private boolean processStructOperListBlock(boolean isNCS) throws Exception {
586     parseLoopParametersFor((isNCS ? FAMILY_NCS : FAMILY_OPER), isNCS ? ncsoperFields : operFields);
587     float[] m = new float[16];
588     m[15] = 1;
589     while (cifParser.getData()) {
590       int count = 0;
591       String id = null;
592       String xyz = null;
593       int n = cifParser.getColumnCount();
594       for (int i = 0; i < n; ++i) {
595         int p = fieldProperty(i);
596         switch (p) {
597         case NONE:
598           break;
599         case OPER_ID:
600           id = field;
601           break;
602         case OPER_XYZ:
603           xyz = field;
604           break;
605         default:
606           m[p] = parseFloatStr(field);
607           ++count;
608         }
609       }
610       if (id != null && (count == 12 || xyz != null && symmetry != null)) {
611         Logger.info((isNCS ? "noncrystallographic symmetry operator " : "assembly operator ") + id + " " + xyz);
612         M4 m4 = new M4();
613         if (count != 12) {
614           symmetry.getMatrixFromString(xyz, m, false, 0);
615           m[3] *= symmetry.getUnitCellInfoType(SimpleUnitCell.INFO_A) / 12;
616           m[7] *= symmetry.getUnitCellInfoType(SimpleUnitCell.INFO_B) / 12;
617           m[11] *= symmetry.getUnitCellInfoType(SimpleUnitCell.INFO_C) / 12;
618         }
619         m4.setA(m);
620         addMatrix(id, m4, isNCS);
621       }
622     }
623     return true;
624   }
625 
addMatrix(String id, M4 m4, boolean isNCS)626   protected void addMatrix(String id, M4 m4, boolean isNCS) {
627     if (isNCS) {
628       if (m4.equals(mident))
629         return;
630       m4.m33 = 0; // flag for normalization
631       if (lstNCS == null)
632         lstNCS = new Lst<M4>();
633       lstNCS.addLast(m4);
634     } else {
635       if (htBiomts == null)
636         htBiomts = new Hashtable<String, M4>();
637       htBiomts.put(id, m4);
638     }
639   }
640 
641   ////////////////////////////////////////////////////////////////
642   // HETATM identity
643   ////////////////////////////////////////////////////////////////
644 
645   final private static byte CHEM_COMP_ID = 0;
646   final private static byte CHEM_COMP_NAME = 1;
647 
648   final private static String FAMILY_CHEMCOMP_CAT = "_chem_comp.";
649 
650   final private static String[] chemCompFields = {
651     "_chem_comp_id",
652     "_chem_comp_name"
653   };
654 
655   /**
656    *
657    * a general name definition field. Not all hetero
658    *
659    * @return true if successful; false to skip
660    *
661    * @throws Exception
662    */
processChemCompLoopBlock()663   private boolean processChemCompLoopBlock() throws Exception {
664     parseLoopParameters(chemCompFields);
665     String groupName, hetName;
666     while (cifParser.getData())
667       if (!isNull(groupName = getField(CHEM_COMP_ID))
668           && !isNull(hetName = getField(CHEM_COMP_NAME)))
669         addHetero(groupName, hetName, true, true);
670     return true;
671   }
672 
673 //  final private static byte NONPOLY_NAME = 0;
674 //  final private static byte NONPOLY_COMP_ID = 1;
675 //
676 //  private static final String FAMILY_PDBX_NONPOLY_CAT = "_pdbx_entity_nonpoly.";
677 //
678 //  final private static String[] nonpolyFields = {
679 //      "_pdbx_entity_nonpoly_name",
680 //      "_pdbx_entity_nonpoly_comp_id", };
681 //
682 //
683 //  /**
684 //   *
685 //   * a HETERO name definition field. Maybe not all hetero? nonpoly?
686 //   *
687 //   * @return true if successful; false to skip
688 //   *
689 //   * @throws Exception
690 //   */
691 //  private boolean processNonpolyLoopBlock() throws Exception {
692 //    parseLoopParameters(nonpolyFields);
693 //    String groupName, hetName;
694 //    while (parser.getData()) {
695 //      if (isNull(groupName = getField(NONPOLY_COMP_ID))
696 //          || isNull(hetName = getField(NONPOLY_NAME)))
697 //        return false;
698 //      addHetero(groupName, hetName, true);
699 //    }
700 //    return true;
701 //  }
702 
addHetero(String groupName, String hetName, boolean doCheck, boolean addNote)703   protected void addHetero(String groupName, String hetName, boolean doCheck, boolean addNote) {
704     if (doCheck && !vwr.getJBR().isHetero(groupName))
705       return;
706     if (htHetero == null)
707       htHetero = new Hashtable<String, String>();
708     if (doCheck && htHetero.containsKey(groupName))
709       return;
710     htHetero.put(groupName, hetName);
711     if (addNote)
712       appendLoadNote(groupName + " = " + hetName);
713   }
714 
715   ////////////////////////////////////////////////////////////////
716   // helix and turn structure data
717   ////////////////////////////////////////////////////////////////
718 
719   final private static byte CONF_TYPE_ID = 0;
720   final private static byte BEG_ASYM_ID = 1;
721   final private static byte BEG_SEQ_ID = 2;
722   final private static byte BEG_INS_CODE = 3;
723   final private static byte END_ASYM_ID = 4;
724   final private static byte END_SEQ_ID = 5;
725   final private static byte END_INS_CODE = 6;
726   final private static byte STRUCT_ID = 7;
727   final private static byte SERIAL_NO = 8;
728   final private static byte HELIX_CLASS = 9;
729 
730   final private static String FAMILY_STRUCTCONF_CAT = "_struct_conf.";
731 
732   final private static String FAMILY_STRUCTCONF = "_struct_conf";
733   final private static String[] structConfFields = {
734       "*_conf_type_id",
735       "*_beg_auth_asym_id",
736       "*_beg_auth_seq_id",
737       "*_pdbx_beg_pdb_ins_code",
738       "*_end_auth_asym_id",
739       "*_end_auth_seq_id",
740       "*_pdbx_end_pdb_ins_code",
741       "*_id",
742       "*_pdbx_pdb_helix_id",
743       "*_pdbx_pdb_helix_class" };
744 
745   /**
746    * identifies ranges for HELIX and TURN
747    *
748    * @return true if successful; false to skip
749    * @throws Exception
750    */
processStructConfLoopBlock()751   private boolean processStructConfLoopBlock() throws Exception {
752     if (ignoreStructure) {
753       cifParser.skipLoop(false);
754       return false;
755     }
756     parseLoopParametersFor(FAMILY_STRUCTCONF, structConfFields);
757     if (!checkAllFieldsPresent(structConfFields, -1, true)) {
758       cifParser.skipLoop(true);
759       return false;
760     }
761     while (cifParser.getData()) {
762       Structure structure = new Structure(-1, STR.HELIX, STR.HELIX, null, 0, 0, null);
763 
764       String type = getField(CONF_TYPE_ID);
765       if (type.startsWith("TURN"))
766         structure.structureType = structure.substructureType = STR.TURN;
767       else if (!type.startsWith("HELX"))
768         structure.structureType = structure.substructureType = STR.NONE;
769       else
770         structure.substructureType = Structure.getHelixType(parseIntStr(getField(HELIX_CLASS)));
771       structure.serialID = parseIntStr(getField(SERIAL_NO));
772       structure.structureID = getField(STRUCT_ID);
773 
774       addStructure(structure);
775     }
776     return true;
777   }
778 
779   ////////////////////////////////////////////////////////////////
780   // sheet structure data
781   ////////////////////////////////////////////////////////////////
782 
addStructure(Structure structure)783   private void addStructure(Structure structure) {
784     structure.startChainID = vwr.getChainID(structure.startChainStr = getField(BEG_ASYM_ID), true);
785     structure.startSequenceNumber = parseIntStr(getField(BEG_SEQ_ID));
786     structure.startInsertionCode = getField(BEG_INS_CODE).charAt(0);
787     structure.endChainID = vwr.getChainID(structure.endChainStr = getField(END_ASYM_ID), true);
788     structure.endSequenceNumber = parseIntStr(getField(END_SEQ_ID));
789     structure.endInsertionCode = getField(END_INS_CODE).charAt(0);
790     asc.addStructure(structure);
791   }
792 
793   final private static byte SHEET_ID = 0;
794   final private static byte STRAND_ID = 7;
795 
796   final private static String FAMILY_SHEET_CAT = "_struct_sheet_range.";
797 
798   final private static String FAMILY_SHEET = "_struct_sheet_range";
799   final private static String[] structSheetRangeFields = {
800       "*_sheet_id",
801       "*_beg_auth_asym_id",
802       "*_beg_auth_seq_id",
803       "*_pdbx_beg_pdb_ins_code",
804       "*_end_auth_asym_id",
805       "*_end_auth_seq_id",
806       "*_pdbx_end_pdb_ins_code",
807       "*_id"
808   };
809 
810   /**
811    *
812    * identifies sheet ranges
813    *
814    * @return true if successful; false to skip
815    *
816    * @throws Exception
817    */
processStructSheetRangeLoopBlock()818   private boolean processStructSheetRangeLoopBlock() throws Exception {
819     if (ignoreStructure) {
820       cifParser.skipLoop(false);
821       return false;
822     }
823     parseLoopParametersFor(FAMILY_SHEET, structSheetRangeFields);
824     if (!checkAllFieldsPresent(structSheetRangeFields, -1, true)) {
825       cifParser.skipLoop(true);
826       return false;
827     }
828     while (cifParser.getData())
829       addStructure(new Structure(-1, STR.SHEET, STR.SHEET, getField(SHEET_ID),
830           parseIntStr(getField(STRAND_ID)), 1, null));
831     return true;
832   }
833 
834   final private static byte SITE_ID = 0;
835   final private static byte SITE_COMP_ID = 1;
836   final private static byte SITE_ASYM_ID = 2;
837   final private static byte SITE_SEQ_ID = 3;
838   final private static byte SITE_INS_CODE = 4; //???
839 
840   final private static String FAMILY_STRUCSITE_CAT = "_struct_site_gen.";
841 
842   final private static String FAMILY_STRUCSITE = "_struct_site_gen";
843   final private static String[] structSiteFields = {
844       "*_site_id",
845       "*_auth_comp_id",
846       "*_auth_asym_id",
847       "*_auth_seq_id",
848       "*_label_alt_id", //should be an insertion code, not an alt ID?
849   };
850 
851   //  loop_
852   //  _struct_site_gen.id
853   //  _struct_site_gen.site_id
854   //  _struct_site_gen.pdbx_num_res
855   //  _struct_site_gen.label_comp_id
856   //  _struct_site_gen.label_asym_id
857   //  _struct_site_gen.label_seq_id
858   //  _struct_site_gen.auth_comp_id
859   //  _struct_site_gen.auth_asym_id
860   //  _struct_site_gen.auth_seq_id
861   //  _struct_site_gen.label_atom_id
862   //  _struct_site_gen.label_alt_id
863   //  _struct_site_gen.symmetry
864   //  _struct_site_gen.details
865   //  1 CAT 5 GLN A 92  GLN A 92  . . ? ?
866   //  2 CAT 5 GLU A 58  GLU A 58  . . ? ?
867   //  3 CAT 5 HIS A 40  HIS A 40  . . ? ?
868   //  4 CAT 5 TYR A 38  TYR A 38  . . ? ?
869   //  5 CAT 5 PHE A 100 PHE A 100 . . ? ?
870   //  #
871 
872   /**
873    *
874    * identifies structure sites
875    *
876    * @return true if successful; false to skip
877    *
878    * @throws Exception
879    */
processStructSiteBlock()880   private boolean processStructSiteBlock() throws Exception {
881     parseLoopParametersFor(FAMILY_STRUCSITE, structSiteFields);
882     Map<String, Object> htSite = null;
883     htSites = new Hashtable<String, Map<String, Object>>();
884     String seqNum, resID;
885     while (cifParser.getData()) {
886       if (isNull(seqNum = getField(SITE_SEQ_ID))
887           || isNull(resID = getField(SITE_COMP_ID)))
888         continue;
889       String siteID = getField(SITE_ID);
890       htSite = htSites.get(siteID);
891       if (htSite == null) {
892         htSite = new Hashtable<String, Object>();
893         htSite.put("groups", "");
894         htSites.put(siteID, htSite);
895       }
896       String insCode = getField(SITE_INS_CODE);
897       String chainID = getField(SITE_ASYM_ID);
898       String group = "[" + resID + "]" + seqNum
899           + (isNull(insCode) ? "" : "^" + insCode)
900           + (isNull(chainID) ? "" : ":" + chainID);
901       String groups = (String) htSite.get("groups");
902       groups += (groups.length() == 0 ? "" : ",") + group;
903       htSite.put("groups", groups);
904     }
905     return true;
906   }
907 
setBiomolecules()908   private void setBiomolecules() {
909     if (assemblyIdAtoms == null && chainAtomCounts == null)
910       return;
911     BS bsAll = new BS();
912     for (int i = vBiomolecules.size(); --i >= 0;) {
913       Map<String, Object> biomolecule = vBiomolecules.get(i);
914       setBiomolecule(biomolecule, (biomolecule == thisBiomolecule ? bsAll : null));
915     }
916     if (isBiomolecule && bsAll.cardinality() < asc.ac) {
917       if (asc.bsAtoms != null)
918         asc.bsAtoms.and(bsAll);
919       else if (!isCourseGrained)
920         asc.bsAtoms = bsAll;
921     }
922   }
923 
924   @SuppressWarnings("unchecked")
setBiomolecule(Map<String, Object> biomolecule, BS bsAll)925   private int setBiomolecule(Map<String, Object> biomolecule, BS bsAll) {
926     Lst<String> biomtchains = (Lst<String>) biomolecule.get("chains");
927     Lst<M4> biomts = (Lst<M4>) biomolecule.get("biomts");
928     Lst<String> operators = (Lst<String>) biomolecule.get("operators");
929     Lst<String> assemblies = (Lst<String>) biomolecule.get("assemblies");
930     P3 sum = new P3();
931     int count = 0;
932     BS bsAtoms = new BS();
933     int nAtomsTotal = 0;
934     boolean isBioCourse = (isBiomolecule && isCourseGrained);
935     for (int i = operators.size(); --i >= 0;) {
936       String[] ops = PT.split(operators.get(i), ",");
937       String[] ids = PT.split(assemblies.get(i), "$");
938       String chainlist = "";
939       int nAtoms = 0;
940       for (int j = 1; j < ids.length; j++) {
941         String id = ids[j];
942         chainlist += ":" + id + ";";
943         if (assemblyIdAtoms != null) {
944           biomolecule.put("asemblyIdAtoms", assemblyIdAtoms);
945           BS bs = assemblyIdAtoms.get(id);
946           if (bs != null) {
947             bsAtoms.or(bs);
948             if (bsAll != null)
949               bsAll.or(bs);
950             nAtoms += bs.cardinality();
951           }
952         } else if (isBioCourse) {
953           P3 asum = chainAtomMap.get(id);
954           if (asum != null) {
955             if (bySymop) {
956               sum.add(asum);
957               count += chainAtomCounts.get(id)[0];
958             } else {
959               createParticle(id);
960               nAtoms++;
961             }
962           }
963         }
964       }
965       if (!isBiomolecule)
966         continue;
967       for (int j = 0; j < ops.length; j++) {
968         M4 m = getOpMatrix(ops[j]);
969         if (m == null)
970           return 0;
971         if (m.equals(mident)) {
972           biomts.add(0, mident);
973           biomtchains.add(0, chainlist);
974         } else {
975           biomts.addLast(m);
976           biomtchains.addLast(chainlist);
977         }
978       }
979       if (bySymop && bsAll != null) {
980         nAtoms = 1;
981         Atom a1 = new Atom();
982         a1.setT(sum);
983         a1.scale(1f / count);
984         a1.radius = 16;
985         asc.addAtom(a1);
986       }
987       nAtoms *= ops.length;
988       nAtomsTotal += nAtoms;
989     }
990     biomolecule.put("atomCount", Integer.valueOf(nAtomsTotal));
991     return nAtomsTotal;
992 
993   }
994 
createParticle(String id)995   private void createParticle(String id) {
996     P3 asum = chainAtomMap.get(id);
997     int c = chainAtomCounts.get(id)[0];
998     Atom a = new Atom();
999     a.setT(asum);
1000     a.scale(1f / c);
1001     a.elementSymbol = "Pt";
1002     setChainID(a, id);
1003     a.radius = 16;
1004     asc.addAtom(a);
1005   }
1006 
getOpMatrix(String ops)1007   private M4 getOpMatrix(String ops) {
1008     if (htBiomts == null)
1009       return M4.newM4(null);
1010     int pt = ops.indexOf("|");
1011     if (pt >= 0) {
1012       M4 m = M4.newM4(htBiomts.get(ops.substring(0, pt)));
1013       m.mul(htBiomts.get(ops.substring(pt + 1)));
1014       return m;
1015     }
1016     return htBiomts.get(ops);
1017   }
1018 
1019   ////////////////////////////////////////////////////////////////
1020   // bond data
1021   ////////////////////////////////////////////////////////////////
1022 
1023   // _STRUCT_CONN is only processed in the presence of _CHEM_CONN (2015 updated cif from EBI)
1024 
1025   final private static byte STRUCT_CONN_ASYM1 = 0;
1026   final private static byte STRUCT_CONN_SEQ1  = 1;
1027   final private static byte STRUCT_CONN_COMP1 = 2;
1028   final private static byte STRUCT_CONN_ATOM1 = 3;
1029   final private static byte STRUCT_CONN_ALT1  = 4;
1030   final private static byte STRUCT_CONN_SYMM1 = 5;
1031   final private static byte STRUCT_CONN_ASYM2 = 6;
1032   final private static byte STRUCT_CONN_SEQ2  = 7;
1033   final private static byte STRUCT_CONN_COMP2 = 8;
1034   final private static byte STRUCT_CONN_ATOM2 = 9;
1035   final private static byte STRUCT_CONN_ALT2  = 10;
1036   final private static byte STRUCT_CONN_SYMM2 = 11;
1037   final private static byte STRUCT_CONN_TYPE  = 12;
1038   final private static byte STRUCT_CONN_ORDER = 13;
1039 
1040 
1041   final private static String FAMILY_STRUCTCONN_CAT = "_struct_conn.";
1042 
1043   final private static String FAMILY_STRUCTCONN = "_struct_conn";
1044   final private static String[] structConnFields = {
1045     "*_ptnr1_auth_asym_id",
1046     "*_ptnr1_auth_seq_id",
1047     "*_ptnr1_auth_comp_id",
1048     "*_ptnr1_label_atom_id",
1049     "*_pdbx_ptnr1_label_alt_id",
1050     "*_ptnr1_symmetry",
1051     "*_ptnr2_auth_asym_id",
1052     "*_ptnr2_auth_seq_id",
1053     "*_ptnr2_auth_comp_id",
1054     "*_ptnr2_label_atom_id",
1055     "*_pdbx_ptnr2_label_alt_id",
1056     "*_ptnr2_symmetry",
1057     "*_conn_type_id",
1058     "*_pdbx_value_order"
1059   };
1060 
1061 //Allowed Value   Details
1062 //covale  covalent bond
1063 //covale_base   covalent modification of a nucleotide base
1064 //covale_phosphate  covalent modification of a nucleotide phosphate
1065 //covale_sugar  covalent modification of a nucleotide sugar
1066 //disulf  disulfide bridge
1067 //metalc  metal coordination
1068 //
1069 //// not used:
1070 //hydrog  hydrogen bond
1071 //mismat  mismatched base pairs
1072 //modres  covalent residue modification
1073 //saltbr  ionic interaction
1074 
1075   private Lst<Object[]> structConnMap;
1076   private String structConnList = "";
1077   private boolean doSetBonds;
1078 
processStructConnLoopBlock()1079   private boolean processStructConnLoopBlock() throws Exception {
1080     parseLoopParametersFor(FAMILY_STRUCTCONN, structConnFields);
1081     while (cifParser.getData()) {
1082       String sym1 = getField(STRUCT_CONN_SYMM1);
1083       String sym2 = getField(STRUCT_CONN_SYMM2);
1084       if (!sym1.equals(sym2) || !isNull(sym1) && !sym1.equals("1_555"))
1085         continue;
1086       String type = getField(STRUCT_CONN_TYPE);
1087       if (!type.startsWith("covale") && !type.equals("disulf")
1088           && !type.equals("metalc"))
1089         continue;
1090       if (htBondMap == null)
1091         htBondMap = new Hashtable<String, Lst<Object[]>>();
1092       String key1 = vwr.getChainID(getField(STRUCT_CONN_ASYM1), true) + getField(STRUCT_CONN_COMP1)
1093           + parseFloatStr(getField(STRUCT_CONN_SEQ1))
1094           + getField(STRUCT_CONN_ATOM1) + getField(STRUCT_CONN_ALT1);
1095       String key2 = vwr.getChainID(getField(STRUCT_CONN_ASYM2), true) + getField(STRUCT_CONN_COMP2)
1096           + parseFloatStr(getField(STRUCT_CONN_SEQ2))
1097           + getField(STRUCT_CONN_ATOM2) + getField(STRUCT_CONN_ALT2);
1098       int order = getBondOrder(getField(STRUCT_CONN_ORDER));
1099       if (structConnMap == null)
1100         structConnMap = new Lst<Object[]>();
1101       structConnMap
1102           .addLast(new Object[] { key1, key2, Integer.valueOf(order) });
1103       if (structConnList.indexOf(key1) < 0)
1104         structConnList += key1;
1105       if (structConnList.indexOf(key2) < 0)
1106         structConnList += key2;
1107     }
1108     return true;
1109   }
1110 
1111   final private static byte CHEM_COMP_BOND_ID = 0;
1112   final private static byte CHEM_COMP_BOND_ATOM_ID_1 = 1;
1113   final private static byte CHEM_COMP_BOND_ATOM_ID_2 = 2;
1114   final private static byte CHEM_COMP_BOND_VALUE_ORDER = 3;
1115   final private static byte CHEM_COMP_BOND_AROMATIC_FLAG = 4;
1116 
1117   final private static String FAMILY_COMPBOND_CAT = "_chem_comp_bond.";
1118 
1119   final private static String FAMILY_COMPBOND = "_chem_comp_bond";
1120   final private static String[] chemCompBondFields = {
1121     "*_comp_id",
1122     "*_atom_id_1",
1123     "*_atom_id_2",
1124     "*_value_order",
1125     "*_pdbx_aromatic_flag"
1126   };
1127 
processCompBondLoopBlock()1128   private boolean processCompBondLoopBlock() throws Exception {
1129     doSetBonds = true;
1130     parseLoopParametersFor(FAMILY_COMPBOND, chemCompBondFields);
1131     while (cifParser.getData()) {
1132       String comp = getField(CHEM_COMP_BOND_ID);
1133       String atom1 = getField(CHEM_COMP_BOND_ATOM_ID_1);
1134       String atom2 = getField(CHEM_COMP_BOND_ATOM_ID_2);
1135       int order = getBondOrder(getField(CHEM_COMP_BOND_VALUE_ORDER));
1136       if ((getField(CHEM_COMP_BOND_AROMATIC_FLAG).charAt(0) == 'Y'))
1137         switch (order) {
1138         case JmolAdapter.ORDER_COVALENT_SINGLE:
1139           order = JmolAdapter.ORDER_AROMATIC_SINGLE;
1140           break;
1141         case JmolAdapter.ORDER_COVALENT_DOUBLE:
1142           order = JmolAdapter.ORDER_AROMATIC_DOUBLE;
1143           break;
1144         }
1145       if (isLigand) {
1146         asc.addNewBondWithOrderA(asc.getAtomFromName(atom1),
1147             asc.getAtomFromName(atom2), order);
1148       } else if (haveHAtoms || htHetero != null && htHetero.containsKey(comp)) {
1149         if (htBondMap == null)
1150           htBondMap = new Hashtable<String, Lst<Object[]>>();
1151         Lst<Object[]> cmap = htBondMap.get(comp);
1152         if (cmap == null)
1153           htBondMap.put(comp, cmap = new Lst<Object[]>());
1154         cmap.addLast(new Object[] { atom1, atom2,
1155             Integer.valueOf(haveHAtoms ? order : 1) });
1156       }
1157     }
1158     return true;
1159   }
1160 
1161   @Override
processSubclassAtom(Atom atom, String assemblyId, String strChain)1162   public boolean processSubclassAtom(Atom atom, String assemblyId, String strChain) {
1163     if (isBiomolecule) {
1164       if (isCourseGrained) {
1165         P3 sum = chainAtomMap.get(assemblyId);
1166         if (sum == null) {
1167           chainAtomMap.put(assemblyId, sum = new P3());
1168           chainAtomCounts.put(assemblyId, new int[1]);
1169         }
1170         chainAtomCounts.get(assemblyId)[0]++;
1171         sum.add(atom);
1172         return false;
1173       }
1174     } else if (byChain) {
1175       if (thisChain != atom.chainID) {
1176         thisChain = atom.chainID;
1177         chainSum = chainAtomMap.get(strChain);
1178         if (chainSum == null) {
1179           chainAtomMap.put(strChain, chainSum = new P3());
1180           chainAtomCounts.put(strChain, chainAtomCount = new int[1]);
1181         }
1182       }
1183       chainSum.add(atom);
1184       chainAtomCount[0]++;
1185       return false;
1186     }
1187     if (assemblyId != null) {
1188       if (assemblyIdAtoms == null)
1189         assemblyIdAtoms = new Hashtable<String, BS>();
1190       BS bs = assemblyIdAtoms.get(assemblyId);
1191       if (bs == null)
1192         assemblyIdAtoms.put(assemblyId, bs = new BS());
1193       bs.set(ac);
1194     }
1195     return true;
1196   }
1197 
1198   private String modelStrings = "";
1199 
1200   protected boolean done;
1201 
1202   @Override
checkPDBModelField(int modelField, int currentModelNo)1203   protected int checkPDBModelField(int modelField, int currentModelNo) throws Exception {
1204     // the model field value is only used if
1205     // it is indicated AFTER the file name in the load command,
1206     // not if we have a MODEL keyword before the file name.
1207 
1208     fieldProperty(modelField);
1209     int modelNo = parseIntStr(field);
1210     return (modelNo == currentModelNo ? modelNo : incrementModel(modelNo));
1211   }
1212 
incrementModel(int modelNo)1213   protected int incrementModel(int modelNo) throws Exception {
1214     boolean isAssembly = (thisDataSetName != null && thisDataSetName.indexOf("-assembly-") >= 0);
1215     if (isAssembly) {
1216       // Files such as http://www.ebi.ac.uk/pdbe/static/entry/download/2lev-assembly-1.cif.gz
1217       // may require sorting if there are multiple models, since the models are by chain, not by model.
1218 
1219       useFileModelNumbers = true;
1220       String key = "," + modelNo + ",";
1221       if (modelStrings.indexOf(key) >= 0) {
1222         requiresSorting = true;
1223       } else {
1224         modelStrings += key;
1225       }
1226     }
1227     if (iHaveDesiredModel && asc.atomSetCount > 0 && !isAssembly) {
1228       done = true;
1229       if (cifParser != null) {
1230         cifParser.skipLoop(false);
1231         // but only this atom loop
1232         skipping = false;
1233       }
1234       continuing = true;
1235       return Integer.MIN_VALUE;
1236     }
1237     int modelNumberToUse = (useFileModelNumbers ? modelNo : ++modelIndex);
1238     setHetero();
1239     newModel(modelNumberToUse);
1240     if (!skipping) {
1241       nextAtomSet();
1242       if (modelMap == null || asc.ac == 0)
1243         modelMap = new Hashtable<String, Integer>();
1244       modelMap.put("" + modelNo, Integer.valueOf(Math.max(0, asc.iSet)));
1245       modelMap
1246           .put("_" + Math.max(0, asc.iSet), Integer.valueOf(modelNo));
1247     }
1248   return modelNo;
1249   }
1250 
setHetero()1251   private void setHetero() {
1252     if (htHetero != null) {
1253       asc.setCurrentModelInfo("hetNames", htHetero);
1254       asc.setInfo("hetNames", htHetero);
1255     }
1256   }
1257 
1258 }
1259