1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2006-10-22 14:12:46 -0500 (Sun, 22 Oct 2006) $
4  * $Revision: 5999 $
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 
25 package org.jmol.adapter.smarter;
26 
27 
28 import java.io.BufferedReader;
29 import java.util.Map;
30 
31 import javajs.api.GenericBinaryDocument;
32 import javajs.api.GenericLineReader;
33 import javajs.util.Lst;
34 import javajs.util.M3;
35 import javajs.util.M4;
36 import javajs.util.OC;
37 import javajs.util.P3;
38 import javajs.util.PT;
39 import javajs.util.Quat;
40 import javajs.util.SB;
41 import javajs.util.T3;
42 import javajs.util.T4;
43 import javajs.util.V3;
44 
45 import org.jmol.api.Interface;
46 import org.jmol.api.JmolAdapter;
47 import org.jmol.api.SymmetryInterface;
48 import javajs.util.BS;
49 import org.jmol.script.SV;
50 import org.jmol.symmetry.Symmetry;
51 import org.jmol.util.BSUtil;
52 import org.jmol.util.Logger;
53 import org.jmol.viewer.Viewer;
54 
55 
56 /*
57  * Notes 9/2006 Bob Hanson
58  *
59  * all parsing functions now moved to org.jmol.util.Parser
60  *
61  * to add symmetry capability to any reader, some or all of the following
62  * methods need to be there:
63  *
64  *  setFractionalCoordinates()
65  *  setSpaceGroupName()
66  *  setUnitCell()
67  *  setUnitCellItem()
68  *  setAtomCoord()
69  *
70  * At the very minimum, you need:
71  *
72  *  setAtomCoord()
73  *
74  * so that:
75  *  (a) atom coordinates can be turned fractional by load parameters
76  *  (b) symmetry can be applied once per model in the file
77  *
78  *  If you know where the end of the atom+bond data are, then you can
79  *  use applySymmetryAndSetTrajectory() once, just before exiting. Otherwise, use it
80  *  twice -- it has a check to make sure it doesn't RUN twice -- once
81  *  at the beginning and once at the end of the model.
82  *
83  * htParams is used for passing information to the readers
84  * and for returning information from the readers
85  *
86  * It won't be null at this stage.
87  *
88  * from Eval or Viewer:
89  *
90  *  applySymmetryToBonds
91  *  atomTypes (for Mol2Reader)
92  *  bsModels
93  *  filter
94  *  firstLastStep
95  *  firstLastSteps
96  *  getHeader
97  *  isTrajectory
98  *  lattice
99  *  manifest (for SmarterJmolAdapter)
100  *  modelNumber
101  *  spaceGroupIndex
102  *  symmetryRange
103  *  unitcell
104  *  packed
105  *
106  * from FileManager:
107  *
108  *  fullPathName
109  *  subFileList (for SmarterJmolAdapter)
110  *
111  * from MdTopReader:
112  *
113  *  isPeriodic
114  *  templateAtomCount
115  *
116  * from MdCrdReader:
117  *
118  *  trajectorySteps
119  *
120  * from Resolver:
121  *
122  *  filteredAtomCount
123  *  ptFile
124  *  readerName
125  *  templateAtomCount
126  *
127  *
128  * from AtomSetCollectionReader:
129  *
130  *  bsFilter
131  *
132  *
133  */
134 
135 public abstract class AtomSetCollectionReader implements GenericLineReader {
136 
137   public final static float ANGSTROMS_PER_BOHR = 0.5291772f; // used by SpartanArchive
138 
139   public boolean isBinary;
140   public boolean debugging;
141   protected boolean requiresBSFilter;
142 
143   public M3 primitiveToCrystal;
144 
145   public AtomSetCollection asc;
146   protected BufferedReader reader;
147   protected GenericBinaryDocument binaryDoc;
148   protected String readerName;
149   public Map<String, Object> htParams;
150   public Lst<P3[]> trajectorySteps;
151   private Object domains;
152   public Object validation, dssr;
153   protected boolean isConcatenated;
154   public String addedData, addedDataKey;
155   public boolean fixJavaFloat = true;
156   public Map<String, Object> thisBiomolecule;
157   public Lst<M4> lstNCS;
158 
159   //protected String parameterData;
160 
161   // buffer
162   public String line, prevline;
163   protected int[] next = new int[1];
164   protected int ptLine;
165 
166   // protected/public state variables
167 
168   protected String latticeType;
169   public int[] latticeCells;
170   public Object fillRange;
171   public boolean doProcessLines;
172   public boolean iHaveUnitCell;
173   public boolean iHaveSymmetryOperators;
174   public boolean continuing = true;
175 
176   public Viewer vwr; // used by GenNBOReader and by CifReader
177 
178   public boolean doApplySymmetry;
179   protected boolean ignoreFileSymmetryOperators;
180   protected boolean isTrajectory;
181   public boolean applySymmetryToBonds;
182   protected boolean doCheckUnitCell;
183   protected boolean getHeader;
184   protected boolean isSequential;
185   public boolean isMolecular; // only for CIF so that it can read multiple unit cells
186   protected int templateAtomCount;
187   public int modelNumber;
188   public int vibrationNumber;
189   public int desiredVibrationNumber = Integer.MIN_VALUE;
190   protected BS bsModels;
191   protected boolean useFileModelNumbers; // PDB, MMCIF only
192   protected boolean havePartialChargeFilter;
193   public String calculationType = "?";
194   protected String sgName;
195   protected boolean ignoreFileUnitCell;
196   protected boolean ignoreFileSpaceGroupName;
197   public float[] unitCellParams; //0-5 a b c alpha beta gamma; 6-21 matrix c->f
198   protected int desiredModelNumber = Integer.MIN_VALUE;
199   public SymmetryInterface symmetry;
200   protected OC out;
201   protected boolean iHaveFractionalCoordinates;
202   public boolean doPackUnitCell;
203   protected P3 ptSupercell;
204   protected boolean mustFinalizeModelSet;
205   protected boolean forcePacked;
206   public float packingError = 0.02f;
207   protected boolean rotateHexCell; // aflow CIF reader only
208   protected boolean isPrimitive; // VASP POSCAR reasder
209   public int modDim; // modulation dimension
210 
211 
212   // private state variables
213 
214   private SB loadNote = new SB();
215   public boolean doConvertToFractional;
216   boolean fileCoordinatesAreFractional;
217   boolean merging;
218   float symmetryRange;
219   private int[] firstLastStep;
220   private int lastModelNumber = Integer.MAX_VALUE;
221   public int desiredSpaceGroupIndex = -1;
222   protected P3 fileScaling;
223   protected float latticeScaling = Float.NaN;
224   protected P3 fileOffset;
225   private P3 fileOffsetFractional;
226   protected P3 unitCellOffset;
227   private boolean unitCellOffsetFractional;
228   private Lst<String> moreUnitCellInfo;
229   public T3 paramsLattice;
230   public boolean paramsCentroid;
231   private boolean paramsPacked;
232 
233 
234   protected String filePath;
235   protected String fileName;
236 
237   /**
238    *  first atom index for this collection, current modelset.ac
239    */
240   public int baseAtomIndex;
241 
242   public int baseBondIndex;
243 
244   protected int stateScriptVersionInt = Integer.MAX_VALUE; // for compatibility PDB reader Jmol 12.0.RC24 fix
245   // http://jmol.svn.sourceforge.net/viewvc/jmol/trunk/Jmol/src/org/jmol/adapter/readers/cifpdb/PdbReader.java?r1=13502&r2=13525
246 
setup(String fullPath, Map<String, Object> htParams, Object readerOrDocument)247   protected void setup(String fullPath, Map<String, Object> htParams, Object readerOrDocument) {
248     setupASCR(fullPath, htParams, readerOrDocument);
249   }
250 
setupASCR(String fullPath, Map<String, Object> htParams, Object readerOrDocument)251   protected void setupASCR(String fullPath, Map<String, Object> htParams, Object readerOrDocument) {
252     if (fullPath == null)
253       return;
254     debugging = Logger.debugging;
255     this.htParams = htParams;
256     filePath = "" + htParams.get("fullPathName");
257     int i = filePath.lastIndexOf('/');
258     fileName = filePath.substring(i + 1);
259     if (readerOrDocument instanceof BufferedReader)
260       this.reader = (BufferedReader) readerOrDocument;
261     else if (readerOrDocument instanceof GenericBinaryDocument)
262       binaryDoc = (GenericBinaryDocument) readerOrDocument;
263   }
264 
readData()265   Object readData() throws Exception {
266     initialize();
267     asc = new AtomSetCollection(readerName, this, null, null);
268     try {
269       initializeReader();
270       if (binaryDoc == null) {
271         if (line == null && continuing)
272           rd();
273         while (line != null && continuing)
274           if (checkLine())
275             rd();
276       } else {
277         binaryDoc.setOutputChannel(out);
278         processBinaryDocument();
279       }
280       finalizeSubclassReader(); // upstairs
281       if (!isFinalized)
282         finalizeReaderASCR();
283     } catch (Throwable e) {
284       Logger.info("Reader error: " + e);
285         e.printStackTrace();
286       setError(e);
287     }
288     if (reader != null)
289       reader.close();
290     if (binaryDoc != null)
291       binaryDoc.close();
292     return finish();
293   }
294 
fixBaseIndices()295   private void fixBaseIndices() {
296     try {
297     int baseModelIndex = ((Integer) htParams.get("baseModelIndex")).intValue();
298     baseAtomIndex += asc.ac;
299     baseBondIndex += asc.bondCount;
300     baseModelIndex += asc.atomSetCount;
301     htParams.put("baseAtomIndex", Integer.valueOf(baseAtomIndex));
302     htParams.put("baseBondIndex", Integer.valueOf(baseBondIndex));
303     htParams.put("baseModelIndex", Integer.valueOf(baseModelIndex));
304     } catch (Exception e) {
305       // ignore
306     }
307   }
308 
readDataObject(Object node)309   protected Object readDataObject(Object node) throws Exception {
310     initialize();
311     asc = new AtomSetCollection(readerName, this, null, null);
312     initializeReader();
313     processDOM(node);
314     return finish();
315   }
316 
317   /**
318    *
319    * @param DOMNode
320    */
processDOM(Object DOMNode)321   protected void processDOM(Object DOMNode) {
322     // XML readers only
323   }
324 
325   /**
326    * @throws Exception
327    */
processBinaryDocument()328   protected void processBinaryDocument() throws Exception {
329     // Binary readers only
330   }
331 
initializeReader()332   protected void initializeReader() throws Exception {
333     // reader-dependent
334   }
335 
336   /**
337    * @return true if need to read new line
338    * @throws Exception
339    *
340    */
checkLine()341   protected boolean checkLine() throws Exception {
342     // reader-dependent
343     return true;
344   }
345 
346   /**
347    * sets continuing and doProcessLines
348    *
349    * @return TRUE if continuing, FALSE if not
350    *
351    */
checkLastModel()352   public boolean checkLastModel() {
353     if (isLastModel(modelNumber) && doProcessLines)
354       return (continuing = doProcessLines = false);
355     doProcessLines = false;
356     return true;
357   }
358 
359   /**
360    * after reading a model, Q: Is this the last model?
361    *
362    * @param modelNumber
363    * @return  Yes/No
364    */
isLastModel(int modelNumber)365   public boolean isLastModel(int modelNumber) {
366     return (desiredModelNumber > 0 || modelNumber >= lastModelNumber);
367   }
368 
appendLoadNote(String info)369   public void appendLoadNote(String info) {
370     if (info == null) {
371       loadNote = new SB();
372       return;
373     }
374     loadNote.append(info).append("\n");
375     Logger.info(info);
376   }
377 
378   @SuppressWarnings("unchecked")
initializeTrajectoryFile()379   protected void initializeTrajectoryFile() {
380     // add a dummy atom, just so not "no atoms found"
381     asc.addAtom(new Atom());
382     trajectorySteps = (Lst<P3[]>) htParams.get("trajectorySteps");
383     if (trajectorySteps == null)
384       htParams.put("trajectorySteps", trajectorySteps = new  Lst<P3[]>());
385   }
386 
387   /**
388    * optional reader-specific method run first.
389    * @throws Exception
390    */
finalizeSubclassReader()391   protected void finalizeSubclassReader() throws Exception {
392     // can be customized
393   }
394 
395   protected boolean isFinalized;
396 
finalizeReaderASCR()397   protected void finalizeReaderASCR() throws Exception {
398     isFinalized = true;
399     if (asc.atomSetCount > 0) {
400       if (asc.atomSetCount == 1) {
401         asc.setCurrentModelInfo("dbName", htParams.get("dbName"));
402         asc.setCurrentModelInfo("auxFiles", htParams.get("auxFiles"));
403       }
404       applySymmetryAndSetTrajectory();
405       asc.finalizeStructures();
406       if (doCentralize)
407         asc.centralize();
408       if (fillRange != null)// && previousUnitCell == null)
409         asc.setInfo("boundbox", fillRange);
410       Map<String, Object> info = asc.getAtomSetAuxiliaryInfo(0);
411       if (info != null) {
412         if (domains != null) {
413           asc.setGlobalBoolean(AtomSetCollection.GLOBAL_DOMAINS);
414           String s = ((SV) domains).getMapKeys(2, true);
415           int pt = s.indexOf("{ ", 2);
416           if (pt >= 0)
417             s = s.substring(pt + 2);
418           pt = s.indexOf("_metadata");
419           if (pt < 0)
420             pt = s.indexOf("metadata");
421           if (pt >= 0)
422             s = s.substring(0, pt);
423           s = PT.rep(PT.replaceAllCharacters(s, "{}", "").trim(), "\n", "\n  ")
424               + "\n\nUse SHOW DOMAINS for details.";
425           appendLoadNote("\nDomains loaded:\n   " + s);
426           for (int i = asc.atomSetCount; --i >= 0;) {
427             info = asc.getAtomSetAuxiliaryInfo(i);
428             info.put("domains", domains);
429           }
430         }
431         if (validation != null) {
432           for (int i = asc.atomSetCount; --i >= 0;) {
433             info = asc.getAtomSetAuxiliaryInfo(i);
434             info.put("validation", validation);
435           }
436         }
437         if (dssr != null) {
438           info.put("dssrJSON", Boolean.TRUE);
439           for (int i = asc.atomSetCount; --i >= 0;) {
440             info = asc.getAtomSetAuxiliaryInfo(i);
441             info.put("dssr", dssr);
442           }
443         }
444       }
445     }
446     if (!fixJavaFloat)
447       asc.setInfo("legacyJavaFloat", Boolean.TRUE);
448     setLoadNote();
449   }
450 
451   /////////////////////////////////////////////////////////////////////////////////////
452 
setLoadNote()453   protected String setLoadNote() {
454     String s = loadNote.toString();
455     if (loadNote.length() > 0)
456       asc.setInfo("modelLoadNote", s);
457     return s;
458   }
459 
setIsPDB()460   public void setIsPDB() {
461     asc.setGlobalBoolean(AtomSetCollection.GLOBAL_ISPDB);
462     if (htParams.get("pdbNoHydrogens") != null)
463       asc.setInfo("pdbNoHydrogens",
464           htParams.get("pdbNoHydrogens"));
465     if (checkFilterKey("ADDHYDROGENS"))
466       asc.setInfo("pdbAddHydrogens",Boolean.TRUE);
467   }
468 
setModelPDB(boolean isPDB)469   protected void setModelPDB(boolean isPDB) {
470     if (isPDB)
471       asc.setGlobalBoolean(AtomSetCollection.GLOBAL_ISPDB);
472     else
473       asc.clearGlobalBoolean(AtomSetCollection.GLOBAL_ISPDB);
474     asc.setCurrentModelInfo("isPDB", isPDB ? Boolean.TRUE : null);
475   }
476 
finish()477   private Object finish() {
478     String s = (String) htParams.get("loadState");
479     asc.setInfo("loadState",
480         s == null ? "" : s);
481     s = (String) htParams.get("smilesString");
482     if (s != null)
483       asc.setInfo("smilesString", s);
484     if (!htParams.containsKey("templateAtomCount"))
485       htParams.put("templateAtomCount", Integer.valueOf(asc
486           .ac));
487     if (bsFilter != null) {
488       htParams.put("filteredAtomCount", Integer.valueOf(BSUtil
489           .cardinalityOf(bsFilter)));
490       htParams.put("bsFilter", bsFilter);
491     }
492     if (!calculationType.equals("?"))
493       asc.setInfo("calculationType",
494           calculationType);
495 
496     String name = asc.fileTypeName;
497     String fileType = name;
498     if (fileType.indexOf("(") >= 0)
499       fileType = fileType.substring(0, fileType.indexOf("("));
500     for (int i = asc.atomSetCount; --i >= 0;) {
501       asc.setModelInfoForSet("fileName", filePath, i);
502       asc.setModelInfoForSet("fileType", fileType, i);
503     }
504     asc.freeze(reverseModels);
505     if (asc.errorMessage != null)
506       return asc.errorMessage + "\nfor file " + filePath
507           + "\ntype " + name;
508     if ((asc.bsAtoms == null ? asc.ac == 0
509         : asc.bsAtoms.nextSetBit(0) < 0)
510         && fileType.indexOf("DataOnly") < 0 && asc.atomSetInfo.get("dataOnly") == null)
511       return "No atoms found\nfor file " + filePath + "\ntype " + name;
512     fixBaseIndices();
513     return asc;
514   }
515 
516   /**
517    * @param e
518    */
setError(Throwable e)519   private void setError(Throwable e) {
520     String s = e.getMessage();
521     if (line == null)
522       asc.errorMessage = "Error reading file at end of file \n" + s;
523     else
524       asc.errorMessage = "Error reading file at line " + ptLine + ":\n" + line
525           + "\n" + s;
526     e.printStackTrace();
527   }
528 
529   @SuppressWarnings("unchecked")
initialize()530   private void initialize() {
531     if (htParams.containsKey("baseAtomIndex"))
532       baseAtomIndex = ((Integer) htParams.get("baseAtomIndex")).intValue();
533     if (htParams.containsKey("baseBondIndex"))
534       baseBondIndex = ((Integer) htParams.get("baseBondIndex")).intValue();
535     initializeSymmetry();
536     vwr = (Viewer) htParams.remove("vwr"); // don't pass this on to user
537     if (htParams.containsKey("stateScriptVersionInt"))
538       stateScriptVersionInt = ((Integer) htParams.get("stateScriptVersionInt"))
539           .intValue();
540     Object o = htParams.get("packingError");
541     if (o != null)
542       packingError = ((Float) o).floatValue();
543     else if (htParams.get("legacyJavaFloat") != null) {
544       // earlier versions were not fully JavaScript compatible
545       // because XtalSymmetry.isWithinUnitCell was giving different answers
546       // for floats (Java) as for doubles (JavaScript).
547       fixJavaFloat = false;
548     }
549     merging = htParams.containsKey("merging");
550     getHeader = htParams.containsKey("getHeader");
551     isSequential = htParams.containsKey("isSequential");
552     readerName = (String) htParams.get("readerName");
553     if (htParams.containsKey("outputChannel"))
554       out = (OC) htParams.get("outputChannel");
555     //parameterData = (String) htParams.get("parameterData");
556     if (htParams.containsKey("vibrationNumber"))
557       desiredVibrationNumber = ((Integer) htParams.get("vibrationNumber"))
558           .intValue();
559     else if (htParams.containsKey("modelNumber"))
560       desiredModelNumber = ((Integer) htParams.get("modelNumber")).intValue();
561     applySymmetryToBonds = htParams.containsKey("applySymmetryToBonds");
562     bsFilter = (requiresBSFilter ? (BS) htParams.get("bsFilter") : null);
563     setFilter(null);
564     fillRange = htParams.get("fillRange");
565     if (strSupercell != null) {
566       if (!checkFilterKey("NOPACK"))
567         forcePacked = true;
568     }
569     o = htParams.get("supercell");
570     if (o instanceof P3) {
571       P3 s = ptSupercell = (P3) o; // only used by CASTEP phonon reader now
572       strSupercell = ((int) s.x) + "a," +((int) s.y) + "b," + ((int) s.z) + "c";
573     } else if (o instanceof String) {
574       strSupercell = (String) o;
575     }
576     // ptFile < 0 indicates just one file being read
577     // ptFile >= 0 indicates multiple files are being loaded
578     // if the file is not the first read in the LOAD command, then
579     // we look to see if it was loaded using LOAD ... "..." COORD ....
580     int ptFile = (htParams.containsKey("ptFile") ? ((Integer) htParams
581         .get("ptFile")).intValue() : -1);
582     isTrajectory = htParams.containsKey("isTrajectory");
583     if (ptFile > 0 && htParams.containsKey("firstLastSteps")) {
584       Object val = ((Lst<Object>) htParams.get("firstLastSteps"))
585           .get(ptFile - 1);
586       if (val instanceof BS) {
587         bsModels = (BS) val;
588       } else {
589         firstLastStep = (int[]) val;
590       }
591     } else if (htParams.containsKey("firstLastStep")) {
592       firstLastStep = (int[]) htParams.get("firstLastStep");
593     } else if (htParams.containsKey("bsModels")) {
594       bsModels = (BS) htParams.get("bsModels");
595     }
596     useFileModelNumbers = htParams.containsKey("useFileModelNumbers") || checkFilterKey("USEFILEMODELNUMBERS");
597     if (htParams.containsKey("templateAtomCount"))
598       templateAtomCount = ((Integer) htParams.get("templateAtomCount"))
599           .intValue();
600     if (bsModels != null || firstLastStep != null)
601       desiredModelNumber = Integer.MIN_VALUE;
602     if (bsModels == null && firstLastStep != null) {
603       if (firstLastStep[0] < 0)
604         firstLastStep[0] = 0;
605       if (firstLastStep[2] == 0 || firstLastStep[1] < firstLastStep[0])
606         firstLastStep[1] = -1;
607       if (firstLastStep[2] < 1)
608         firstLastStep[2] = 1;
609       bsModels = BSUtil.newAndSetBit(firstLastStep[0]);
610       if (firstLastStep[1] > firstLastStep[0]) {
611         for (int i = firstLastStep[0]; i <= firstLastStep[1]; i += firstLastStep[2])
612           bsModels.set(i);
613       }
614     }
615     if (bsModels != null && (firstLastStep == null || firstLastStep[1] != -1))
616       lastModelNumber = bsModels.length();
617 
618     symmetryRange = (htParams.containsKey("symmetryRange") ? ((Float) htParams
619         .get("symmetryRange")).floatValue() : 0);
620     paramsLattice = (T3) htParams.get("lattice");
621     paramsCentroid = htParams.containsKey("centroid");
622     paramsPacked = htParams.containsKey("packed");
623     initializeSymmetryOptions();
624     //this flag FORCES symmetry -- generally if coordinates are not fractional,
625     //we may note the unit cell, but we do not apply symmetry
626     //with this flag, we convert any nonfractional coordinates to fractional
627     //if a unit cell is available.
628 
629     if (htParams.containsKey("spaceGroupIndex")) {
630       // three options include:
631       // = -1: normal -- use operators if present or name if not
632       // = -2: user is supplying operators or name
633       // >=0: spacegroup fully determined
634       // = -999: ignore -- just the operators
635 
636       desiredSpaceGroupIndex = ((Integer) htParams.get("spaceGroupIndex"))
637           .intValue();
638       if (desiredSpaceGroupIndex == -2)
639         sgName = (String) htParams.get("spaceGroupName");
640       ignoreFileSpaceGroupName = (desiredSpaceGroupIndex == -2 || desiredSpaceGroupIndex >= 0);
641       ignoreFileSymmetryOperators = (desiredSpaceGroupIndex != -1);
642     }
643     if (htParams.containsKey("unitCellOffset")) {
644       fileScaling = P3.new3(1, 1, 1);
645       fileOffset = (P3) htParams.get("unitCellOffset");
646       fileOffsetFractional = P3.newP(fileOffset);
647       unitCellOffsetFractional = htParams
648           .containsKey("unitCellOffsetFractional");
649     }
650     if (htParams.containsKey("unitcell")) {
651       float[] fParams = (float[]) htParams.get("unitcell");
652       if (merging)
653         setFractionalCoordinates(true);
654       if (fParams.length == 9) {
655         // these are vectors
656         addExplicitLatticeVector(0, fParams, 0);
657         addExplicitLatticeVector(1, fParams, 3);
658         addExplicitLatticeVector(2, fParams, 6);
659       } else {
660         setUnitCell(fParams[0], fParams[1], fParams[2], fParams[3], fParams[4],
661             fParams[5]);
662       }
663       ignoreFileUnitCell = iHaveUnitCell;
664       if (merging && !iHaveUnitCell)
665         setFractionalCoordinates(false);
666       // with appendNew == false and UNITCELL parameter, we assume fractional coordinates
667     }
668     domains = htParams.get("domains");
669     validation = htParams.get("validation");
670     dssr = htParams.get("dssr");
671     isConcatenated = htParams.containsKey("concatenate");
672   }
673 
initializeSymmetryOptions()674   protected void initializeSymmetryOptions() {
675     latticeCells = new int[4];
676     doApplySymmetry = false;
677     T3 pt = paramsLattice;
678     if (pt == null || pt.length() == 0) {
679       if (!forcePacked && strSupercell == null)
680         return;
681       pt = P3.new3(1, 1, 1);
682     }
683     latticeCells[0] = (int) pt.x;
684     latticeCells[1] = (int) pt.y;
685     latticeCells[2] = (int) pt.z;
686     if (pt instanceof T4)
687       latticeCells[3] = (int) ((T4) pt).w;
688     doCentroidUnitCell = paramsCentroid;
689     if (doCentroidUnitCell && (latticeCells[2] == -1 || latticeCells[2] == 0))
690       latticeCells[2] = 1;
691     boolean isPacked = forcePacked || paramsPacked;
692     centroidPacked = doCentroidUnitCell && isPacked;
693     doPackUnitCell = !doCentroidUnitCell && (isPacked || latticeCells[2] < 0);
694     doApplySymmetry = (latticeCells[0] > 0 && latticeCells[1] > 0);
695     //allows for {1 1 1} or {1 1 -1} or {555 555 0|1|-1} (-1  being "packed")
696     if (!doApplySymmetry)
697       latticeCells = new int[3];
698   }
699 
700   protected boolean haveModel;
701 
doGetModel(int modelNumber, String title)702   public boolean doGetModel(int modelNumber, String title) {
703     if (title != null && nameRequired != null && nameRequired.length() > 0
704         && title.toUpperCase().indexOf(nameRequired) < 0)
705           return false;
706     // modelNumber is 1-based, but firstLastStep is 0-based
707     boolean isOK = (bsModels == null ? desiredModelNumber < 1
708         || modelNumber == desiredModelNumber
709         : modelNumber > lastModelNumber ? false : modelNumber > 0
710             && bsModels.get(modelNumber - 1)
711             || haveModel
712             && firstLastStep != null
713             && firstLastStep[1] < 0
714             && (firstLastStep[2] < 2 || (modelNumber - 1 - firstLastStep[0])
715                 % firstLastStep[2] == 0));
716     if (isOK && desiredModelNumber == 0)
717       discardPreviousAtoms();
718     haveModel |= isOK;
719     if (isOK)
720       doProcessLines = true;
721     return isOK;
722   }
723 
discardPreviousAtoms()724   protected void discardPreviousAtoms() {
725     asc.discardPreviousAtoms();
726   }
727 
728   private String previousSpaceGroup;
729   private float[] previousUnitCell;
730 
initializeSymmetry()731   protected final void initializeSymmetry() {
732     previousSpaceGroup = sgName;
733     previousUnitCell = unitCellParams;
734     iHaveUnitCell = ignoreFileUnitCell;
735     if (!ignoreFileUnitCell) {
736       unitCellParams = new float[26];
737       //0-5 a b c alpha beta gamma
738       //6-21 m00 m01... m33 cartesian-->fractional
739       //22-24 supercell.x supercell.y supercell.z
740       //25 scaling
741       for (int i = 25; --i >= 0;)
742         unitCellParams[i] = Float.NaN;
743       unitCellParams[25] = latticeScaling;
744       symmetry = null;
745     }
746     if (!ignoreFileSpaceGroupName)
747       sgName = "unspecified!";
748     doCheckUnitCell = false;
749   }
750 
newAtomSet(String name)751   protected void newAtomSet(String name) {
752     if (asc.iSet >= 0) {
753       asc.newAtomSet();
754       asc.setCollectionName("<collection of "
755           + (asc.iSet + 1) + " models>");
756     } else {
757       asc.setCollectionName(name);
758     }
759     asc.setModelInfoForSet("name", name, Math.max(0, asc.iSet));
760     asc.setAtomSetName(name);
761   }
762 
cloneLastAtomSet(int ac, P3[] pts)763   protected int cloneLastAtomSet(int ac, P3[] pts) throws Exception {
764     int lastAtomCount = asc.getLastAtomSetAtomCount();
765     asc.cloneLastAtomSetFromPoints(ac, pts);
766     if (asc.haveUnitCell) {
767       iHaveUnitCell = true;
768       doCheckUnitCell = true;
769       sgName = previousSpaceGroup;
770       unitCellParams = previousUnitCell;
771     }
772     return lastAtomCount;
773   }
774 
setSpaceGroupName(String name)775   public void setSpaceGroupName(String name) {
776     if (ignoreFileSpaceGroupName || name == null)
777       return;
778     String s = name.trim();
779     if (s.length() == 0 || s.equals("HM:") || s.equals(sgName))
780       return;
781     if (!s.equals("P1"))
782       Logger.info("Setting space group name to " + s);
783     sgName = s;
784   }
785 
setSymmetryOperator(String xyz)786   public int setSymmetryOperator(String xyz) {
787     if (ignoreFileSymmetryOperators)
788       return -1;
789     int isym = asc.getXSymmetry().addSpaceGroupOperation(xyz, true);
790     if (isym < 0)
791       Logger.warn("Skippings symmetry operation " + xyz);
792     iHaveSymmetryOperators = true;
793     return isym;
794   }
795 
796   private int nMatrixElements = 0;
797 
initializeCartesianToFractional()798   private void initializeCartesianToFractional() {
799     for (int i = 0; i < 16; i++)
800       if (!Float.isNaN(unitCellParams[6 + i]))
801         return; //just do this once
802     for (int i = 0; i < 16; i++)
803       unitCellParams[6 + i] = ((i % 5 == 0 ? 1 : 0));
804     nMatrixElements = 0;
805   }
806 
clearUnitCell()807   public void clearUnitCell() {
808     if (ignoreFileUnitCell)
809       return;
810     for (int i = 6; i < 22; i++)
811       unitCellParams[i] = Float.NaN;
812     checkUnitCell(6);
813   }
814 
815   public float[] ucItems;
setUnitCellItem(int i, float x)816   public void setUnitCellItem(int i, float x) {
817     if (ignoreFileUnitCell)
818       return;
819     if (i == 0 && x == 1 && !allow_a_len_1  || i == 3 && x == 0) {
820       if (ucItems == null)
821         ucItems = new float[6];
822       ucItems[i] = x;
823       return;
824     }
825     if (ucItems != null && i < 6)
826       ucItems[i] = x;
827 
828     if (!Float.isNaN(x) && i >= 6 && Float.isNaN(unitCellParams[6]))
829       initializeCartesianToFractional();
830     unitCellParams[i] = x;
831     if (debugging) {
832       Logger.debug("setunitcellitem " + i + " " + x);
833     }
834     if (i < 6 || Float.isNaN(x))
835       iHaveUnitCell = checkUnitCell(6);
836     else if (++nMatrixElements == 12)
837       iHaveUnitCell = checkUnitCell(22);
838   }
839 
840   protected M3 matUnitCellOrientation;
841 
setUnitCell(float a, float b, float c, float alpha, float beta, float gamma)842   public void setUnitCell(float a, float b, float c, float alpha, float beta,
843                           float gamma) {
844     if (ignoreFileUnitCell)
845       return;
846     clearUnitCell();
847     unitCellParams[0] = a;
848     unitCellParams[1] = b;
849     unitCellParams[2] = c;
850     if (alpha != 0)
851       unitCellParams[3] = alpha;
852     if (beta != 0)
853       unitCellParams[4] = beta;
854     if (gamma != 0)
855       unitCellParams[5] = gamma;
856     iHaveUnitCell = checkUnitCell(6);
857   }
858 
addExplicitLatticeVector(int i, float[] xyz, int i0)859   public void addExplicitLatticeVector(int i, float[] xyz, int i0) {
860     if (ignoreFileUnitCell)
861       return;
862     if (i == 0)
863       for (int j = 0; j < 6; j++)
864         unitCellParams[j] = 0;
865     i = 6 + i * 3;
866     unitCellParams[i++] = xyz[i0++];
867     unitCellParams[i++] = xyz[i0++];
868     unitCellParams[i] = xyz[i0];
869     if (Float.isNaN(unitCellParams[0])) {
870       for (i = 0; i < 6; i++)
871         unitCellParams[i] = -1;
872     }
873     iHaveUnitCell = checkUnitCell(15);
874   }
875 
checkUnitCell(int n)876   private boolean checkUnitCell(int n) {
877     for (int i = 0; i < n; i++)
878       if (Float.isNaN(unitCellParams[i]))
879         return false;
880     if (n == 22 && unitCellParams[0] == 1) {
881       if (unitCellParams[1] == 1
882           && unitCellParams[2] == 1
883           && unitCellParams[6] == 1
884           && unitCellParams[11] == 1
885           && unitCellParams[16] == 1
886           )
887         return false;
888       // this is an mmCIF or PDB case for NMR models having
889       // CRYST1    1.000    1.000    1.000  90.00  90.00  90.00 P 1           1
890       // ORIGX1      1.000000  0.000000  0.000000        0.00000
891       // ORIGX2      0.000000  1.000000  0.000000        0.00000
892       // ORIGX3      0.000000  0.000000  1.000000        0.00000
893       // SCALE1      1.000000  0.000000  0.000000        0.00000
894       // SCALE2      0.000000  1.000000  0.000000        0.00000
895       // SCALE3      0.000000  0.000000  1.000000        0.00000
896     }
897     if (doApplySymmetry) {
898       getSymmetry();
899       doConvertToFractional = !fileCoordinatesAreFractional;
900     }
901     //if (but not only if) applying symmetry do we force conversion
902 //    checkUnitCellOffset();
903     return true;
904   }
905 
getSymmetry()906   public SymmetryInterface getSymmetry() {
907     if (!iHaveUnitCell)
908       return null;
909     if (symmetry == null) {
910       getNewSymmetry().setUnitCell(unitCellParams, false);
911       checkUnitCellOffset();
912     }
913     if (symmetry == null) // cif file with no symmetry triggers exception on LOAD {1 1 1}
914       iHaveUnitCell = false;
915     else
916       symmetry.setSpaceGroupName(sgName);
917     return symmetry;
918   }
checkUnitCellOffset()919   private void checkUnitCellOffset() {
920     if (fileOffsetFractional == null || symmetry == null)
921       return;
922     fileOffset.setT(fileOffsetFractional);
923     if (unitCellOffsetFractional != fileCoordinatesAreFractional) {
924       if (unitCellOffsetFractional)
925         symmetry.toCartesian(fileOffset, false);
926       else
927         symmetry.toFractional(fileOffset, false);
928     }
929   }
930 
fractionalizeCoordinates(boolean toFrac)931   protected void fractionalizeCoordinates(boolean toFrac) {
932     if (getSymmetry() == null)
933       return;
934     Atom[] a = asc.atoms;
935     if (toFrac)
936       for (int i = asc.ac; --i >= 0;)
937         symmetry.toFractional(a[i], false);
938     else
939       for (int i = asc.ac; --i >= 0;)
940         symmetry.toCartesian(a[i], false);
941     setFractionalCoordinates(toFrac);
942   }
getNewSymmetry()943   protected SymmetryInterface getNewSymmetry() {
944     return symmetry = (Symmetry) getInterface("org.jmol.symmetry.Symmetry");
945   }
946 
setFractionalCoordinates(boolean TF)947   public void setFractionalCoordinates(boolean TF) {
948     iHaveFractionalCoordinates = fileCoordinatesAreFractional = TF;
949     checkUnitCellOffset();
950   }
951 
952   /////////// FILTER /////////////////
953 
954   protected BS bsFilter;
955   public String filter;
956   public boolean haveAtomFilter;
957   private boolean filterAltLoc;
958   private boolean filterGroup3;
959   private boolean filterChain;
960   private boolean filterAtomName;
961   private boolean filterAtomType;
962   private String filterAtomTypeStr;
963   private String filterAtomNameTerminator = ";";
964   private boolean filterElement;
965   protected boolean filterHetero;
966   private boolean filterEveryNth;
967   String filterSymop;
968   private int filterN;
969   private int nFiltered;
970   private boolean doSetOrientation;
971   protected boolean doCentralize;
972   protected boolean addVibrations;
973   protected boolean useAltNames;
974   protected boolean ignoreStructure;
975   protected boolean isDSSP1;
976   protected boolean allowPDBFilter;
977   public boolean doReadMolecularOrbitals;
978   protected boolean reverseModels;
979   private String nameRequired;
980   public boolean doCentroidUnitCell;
981   public boolean centroidPacked;
982   public String strSupercell;
983 
984 
985   public boolean allow_a_len_1 = false;
986 
987   // ALL:  "CENTER" "REVERSEMODELS"
988   // ALL:  "SYMOP=n"
989   // MANY: "NOVIB" "NOMO"
990   // CASTEP: "CHARGE=HIRSH q={i,j,k};"
991   // CIF: "ASSEMBLY n"
992   // CRYSTAL: "CONV" (conventional), "INPUT"
993   // CSF, SPARTAN: "NOORIENT"
994   // GAMESS-US:  "CHARGE=LOW"
995   // JME, MOL: "NOMIN"
996   // MOL:  "2D"
997   // Molden: "INPUT" "GEOM" "NOGEOM"
998   // MopacArchive: "NOCENTER"
999   // MOReaders: "NBOCHARGES"
1000   // P2N: "ALTNAME"
1001   // PDB: "BIOMOLECULE n;" "NOSYMMETRY"  "CONF n"
1002   // Spartan: "INPUT", "ESPCHARGES"
1003   //
1004 
setFilterAtomTypeStr(String s)1005   protected void setFilterAtomTypeStr(String s) {
1006     // PDB reader TYPE=...
1007     filterAtomTypeStr = s;
1008     filterAtomNameTerminator = "\0";
1009   }
1010 
setFilter(String filter0)1011   protected void setFilter(String filter0) {
1012     if (filter0 == null) {
1013       filter0 = (String) htParams.get("filter");
1014     } else {
1015       // from PDB REMARK350()
1016       bsFilter = null;
1017     }
1018     if (filter0 != null)
1019       filter0 = filter0.toUpperCase();
1020     filter = filter0;
1021     doSetOrientation = !checkFilterKey("NOORIENT");
1022     doCentralize = (!checkFilterKey("NOCENTER") && checkFilterKey("CENTER"));
1023     addVibrations = !checkFilterKey("NOVIB");
1024     ignoreStructure = checkFilterKey("DSSP");
1025     isDSSP1 = checkFilterKey("DSSP1");
1026     doReadMolecularOrbitals = !checkFilterKey("NOMO");
1027     useAltNames = checkFilterKey("ALTNAME");
1028     reverseModels = checkFilterKey("REVERSEMODELS");
1029     allow_a_len_1 =checkFilterKey("TOPOS");
1030 
1031     if (filter == null)
1032       return;
1033     if (checkFilterKey("HETATM")) {
1034       filterHetero = true;
1035       filter = PT.rep(filter, "HETATM", "HETATM-Y");
1036     }
1037     if (checkFilterKey("ATOM")) {
1038       filterHetero = true;
1039       filter = PT.rep(filter, "ATOM", "HETATM-N");
1040     }
1041 
1042     // can't use getFilter() here because form includes a semicolon:
1043     // cell=a+b,a-b,c;0,1/2,1/2
1044     if (checkFilterKey("CELL="))
1045       strSupercell = filter.substring(filter.indexOf("CELL=") + 5).toLowerCase(); // must be last filter option
1046     nameRequired = PT.getQuotedAttribute(filter, "NAME");
1047     if (nameRequired != null) {
1048       if (nameRequired.startsWith("'"))
1049         nameRequired = PT.split(nameRequired, "'")[1];
1050       else if (nameRequired.startsWith("\""))
1051         nameRequired = PT.split(nameRequired, "\"")[1];
1052       filter = PT.rep(filter, nameRequired, "");
1053       filter0 = filter = PT.rep(filter, "NAME=", "");
1054     }
1055     filterAtomName = checkFilterKey("*.") || checkFilterKey("!.");
1056     if (filter.startsWith("_") || filter.indexOf(";_") >= 0)
1057       filterElement = checkFilterKey("_");
1058 
1059     filterGroup3 = checkFilterKey("[");
1060     filterChain = checkFilterKey(":");
1061     filterAltLoc = checkFilterKey("%");
1062     filterEveryNth = checkFilterKey("/=");
1063     if (filterEveryNth)
1064       filterN = parseIntAt(filter, filter.indexOf("/=") + 2);
1065     else if (filter.startsWith("=") || filter.indexOf(";=") >= 0)
1066       filterAtomType = checkFilterKey("=");
1067     if (filterN == Integer.MIN_VALUE)
1068       filterEveryNth = false;
1069     haveAtomFilter = filterAtomName || filterAtomType || filterElement
1070         || filterGroup3 || filterChain || filterAltLoc || filterHetero
1071         || filterEveryNth || checkFilterKey("/=");
1072     if (bsFilter == null) {
1073       // bsFilter is usually null, but from MDTOP it gets set to indicate
1074       // which atoms were selected by the filter. This then
1075       // gets used by COORD files to load just those coordinates
1076       // and it returns the bitset of filtered atoms
1077       bsFilter = new BS();
1078       htParams.put("bsFilter", bsFilter);
1079       filter = (";" + filter + ";").replace(',', ';');
1080       String s = getFilter("LATTICESCALING=");
1081       if (s != null && unitCellParams.length > 25)
1082         unitCellParams[25] = latticeScaling = parseFloatStr(s);
1083       s = getFilter("SYMOP=");
1084       if (s != null)
1085         filterSymop = " " + s + " ";
1086       Logger.info("filtering with " + filter);
1087       if (haveAtomFilter) {
1088         int ipt;
1089         filter1 = filter;
1090         if ((ipt = filter.indexOf("|")) >= 0) {
1091           filter1 = filter.substring(0, ipt).trim() + ";";
1092           filter2 = ";" + filter.substring(ipt).trim();
1093         }
1094       }
1095     }
1096   }
1097 
1098   private String filter1, filter2;
1099 
getFilter(String key)1100   public String getFilter(String key) {
1101     int pt = (filter == null ? -1 : filter.indexOf(key));
1102     return (pt < 0 ? null : filter.substring(pt + key.length(), filter.indexOf(";", pt)));
1103   }
1104 
checkFilterKey(String key)1105   public boolean checkFilterKey(String key) {
1106     return (filter != null && filter.indexOf(key) >= 0);
1107   }
1108 
1109   /**
1110    * @param key
1111    * @return true if the key existed; filter is set null if this is the only key
1112    *
1113    */
1114 
checkAndRemoveFilterKey(String key)1115   public boolean checkAndRemoveFilterKey(String key) {
1116     if (!checkFilterKey(key))
1117       return false;
1118     filter = PT.rep(filter, key, "");
1119     // allows for "!" and ";"
1120     if (filter.length() < 3)
1121       filter = null;
1122     return true;
1123   }
1124 
1125 
1126   /**
1127    * @param atom
1128    * @param iAtom
1129    * @return        true if we want this atom
1130    */
filterAtom(Atom atom, int iAtom)1131   protected boolean filterAtom(Atom atom, int iAtom) {
1132     if (!haveAtomFilter)
1133       return true;
1134     // cif, mdtop, pdb, gromacs, pqr
1135     boolean isOK = checkFilter(atom, filter1);
1136     if (filter2 != null)
1137       isOK |= checkFilter(atom, filter2);
1138     if (isOK && filterEveryNth)
1139       isOK = (((nFiltered++) % filterN) == 0);
1140     bsFilter.setBitTo(iAtom >= 0 ? iAtom : asc.ac, isOK);
1141     return isOK;
1142   }
1143 
1144   /**
1145    *
1146    * @param atom
1147    * @param f
1148    * @return  true if a filter is found
1149    */
checkFilter(Atom atom, String f)1150   private boolean checkFilter(Atom atom, String f) {
1151     return (!filterGroup3 || atom.group3 == null || !filterReject(f, "[",
1152         atom.group3.toUpperCase() + "]"))
1153         && (!filterAtomName || allowAtomName(atom.atomName, f))
1154         && (filterAtomTypeStr == null || atom.atomName == null
1155             || atom.atomName.toUpperCase().indexOf("\0" + filterAtomTypeStr) >= 0)
1156         && (!filterElement || atom.elementSymbol == null || !filterReject(f, "_",
1157             atom.elementSymbol.toUpperCase() + ";"))
1158         && (!filterChain || atom.chainID == 0 || !filterReject(f, ":", ""
1159             + vwr.getChainIDStr(atom.chainID)))
1160         && (!filterAltLoc || atom.altLoc == '\0' || !filterReject(
1161             f, "%", "" + atom.altLoc))
1162         && (!filterHetero || !allowPDBFilter || !filterReject(f, "HETATM",
1163             atom.isHetero ? "-Y" : "-N"));
1164   }
1165 
rejectAtomName(String name)1166   public boolean rejectAtomName(String name) {
1167     return filterAtomName && !allowAtomName(name, filter);
1168   }
1169 
allowAtomName(String atomName, String f)1170   private boolean allowAtomName(String atomName, String f) {
1171     return (atomName == null || !filterReject(f, ".",
1172         atomName.toUpperCase() + filterAtomNameTerminator));
1173   }
1174 
filterReject(String f, String code, String atomCode)1175   protected boolean filterReject(String f, String code, String atomCode) {
1176     return (f.indexOf(code) >= 0
1177         && (f.indexOf("!" + code) >= 0) ==
1178                (f.indexOf(code + atomCode) >= 0)
1179             );
1180   }
1181 
set2D()1182   protected void set2D() {
1183     // MOL and JME - sets for ALL MODELS, but just for ModelLoader.
1184     // It is quite possible that multiple 2D models cannot be loaded as 3D
1185     asc.setInfo("is2D", Boolean.TRUE);
1186     if (!checkFilterKey("NOMIN"))
1187       asc.setInfo("doMinimize",
1188           Boolean.TRUE);
1189     appendLoadNote("This model is 2D. Its 3D structure will be generated.");
1190   }
1191 
doGetVibration(int vibrationNumber)1192   public boolean doGetVibration(int vibrationNumber) {
1193     // vibrationNumber is 1-based
1194     return addVibrations
1195         && (desiredVibrationNumber <= 0 || vibrationNumber == desiredVibrationNumber);
1196   }
1197 
1198   private M3 matRot;
1199 
1200   public MSInterface ms;
1201 
setTransform(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3)1202   public void setTransform(float x1, float y1, float z1, float x2, float y2,
1203                            float z2, float x3, float y3, float z3) {
1204     if (matRot != null || !doSetOrientation)
1205       return;
1206     matRot = new M3();
1207     V3 v = V3.new3(x1, y1, z1);
1208     // rows in Sygress/CAChe and Spartan become columns here
1209     v.normalize();
1210     matRot.setColumnV(0, v);
1211     v.set(x2, y2, z2);
1212     v.normalize();
1213     matRot.setColumnV(1, v);
1214     v.set(x3, y3, z3);
1215     v.normalize();
1216     matRot.setColumnV(2, v);
1217     asc.setInfo("defaultOrientationMatrix", M3.newM3(matRot));
1218     // first two matrix column vectors define quaternion X and XY plane
1219     Quat q = Quat.newM(matRot);
1220     asc.setInfo("defaultOrientationQuaternion", q);
1221     Logger.info("defaultOrientationMatrix = " + matRot);
1222 
1223   }
1224 
1225   /////////////////////////////
1226 
setAtomCoordXYZ(Atom atom, float x, float y, float z)1227   public void setAtomCoordXYZ(Atom atom, float x, float y, float z) {
1228     atom.set(x, y, z);
1229     setAtomCoord(atom);
1230   }
1231 
setAtomCoordScaled(Atom atom, String[] tokens, int i, float f)1232   public Atom setAtomCoordScaled(Atom atom, String[] tokens, int i, float f) {
1233     if (atom == null)
1234       atom = asc.addNewAtom();
1235     setAtomCoordXYZ(atom, parseFloatStr(tokens[i]) * f,
1236         parseFloatStr(tokens[i + 1]) * f, parseFloatStr(tokens[i + 2]) * f);
1237     return atom;
1238   }
1239 
setAtomCoordTokens(Atom atom, String[] tokens, int i)1240   protected void setAtomCoordTokens(Atom atom, String[] tokens, int i) {
1241     setAtomCoordXYZ(atom, parseFloatStr(tokens[i]), parseFloatStr(tokens[i + 1]),
1242           parseFloatStr(tokens[i + 2]));
1243   }
1244 
addAtomXYZSymName(String[] tokens, int i, String sym, String name)1245   public Atom addAtomXYZSymName(String[] tokens, int i, String sym, String name) {
1246     Atom atom = asc.addNewAtom();
1247     if (sym != null)
1248       atom.elementSymbol = sym;
1249     if (name != null)
1250       atom.atomName = name;
1251     setAtomCoordTokens(atom, tokens, i);
1252     return atom;
1253   }
1254 
setAtomCoord(Atom atom)1255   public void setAtomCoord(Atom atom) {
1256     // fileScaling is used by the PLOT command to
1257     // put data into PDB format, preserving name/residue information,
1258     // and still get any xyz data into the allotted column space.
1259     boolean mustFractionalize = (doConvertToFractional && !fileCoordinatesAreFractional && getSymmetry() != null);
1260     if (fileScaling != null) {
1261       atom.x = atom.x * fileScaling.x + fileOffset.x;
1262       atom.y = atom.y * fileScaling.y + fileOffset.y;
1263       atom.z = atom.z * fileScaling.z + fileOffset.z;
1264     }
1265     if (mustFractionalize) {
1266       if (!symmetry.haveUnitCell())
1267         symmetry.setUnitCell(unitCellParams, false);
1268       symmetry.toFractional(atom, false);
1269       iHaveFractionalCoordinates = true;
1270     }
1271     if (fixJavaFloat && fileCoordinatesAreFractional)
1272       PT.fixPtFloats(atom, PT.FRACTIONAL_PRECISION);
1273     doCheckUnitCell = true;
1274   }
1275 
addSites(Map<String, Map<String, Object>> htSites)1276   public void addSites(Map<String, Map<String, Object>> htSites) {
1277     asc.setCurrentModelInfo("pdbSites", htSites);
1278     String sites = "";
1279     for (Map.Entry<String, Map<String, Object>> entry : htSites.entrySet()) {
1280       String name = entry.getKey();
1281       Map<String, Object> htSite = entry.getValue();
1282       char ch;
1283       for (int i = name.length(); --i >= 0;)
1284         if (!PT.isLetterOrDigit(ch = name.charAt(i)) && ch != '\'')
1285           name = name.substring(0, i) + "_" + name.substring(i + 1);
1286       String groups = (String) htSite.get("groups");
1287       if (groups.length() == 0)
1288         continue;
1289       addSiteScript("@site_" + name + " " + groups);
1290       addSiteScript("site_" + name + " = [\"" + PT.rep(groups, ",", "\",\"") + "\"]");
1291       sites += ",\"site_" + name + "\"";
1292     }
1293     if (sites.length() > 0)
1294       addSiteScript("site_list = [" + sites.substring(1) + "]");
1295   }
1296 
applySymmetryAndSetTrajectory()1297   public void applySymmetryAndSetTrajectory() throws Exception {
1298     // overridden in many readers
1299     applySymTrajASCR();
1300   }
1301 
1302   public boolean vibsFractional = false;
1303 
applySymTrajASCR()1304   public SymmetryInterface applySymTrajASCR() throws Exception {
1305     if (forcePacked)
1306       initializeSymmetryOptions();
1307     SymmetryInterface sym = (iHaveUnitCell && doCheckUnitCell ? asc
1308         .getXSymmetry().applySymmetryFromReader(getSymmetry()) : null);
1309     if (sym == null)
1310       asc.setTensors();
1311     if (isTrajectory)
1312       asc.setTrajectory();
1313     if (moreUnitCellInfo != null) {
1314       asc.setCurrentModelInfo("moreUnitCellInfo", moreUnitCellInfo);
1315       moreUnitCellInfo = null;
1316     }
1317     finalizeSubclassSymmetry(sym != null);
1318     //if (sym != null && ptSupercell != null)
1319       //asc.getXSymmetry().finalizeUnitCell(ptSupercell);
1320     initializeSymmetry();
1321     return sym;
1322   }
1323 
1324   /**
1325    * @param haveSymmetry
1326    * @throws Exception
1327    */
finalizeSubclassSymmetry(boolean haveSymmetry)1328   protected void finalizeSubclassSymmetry(boolean haveSymmetry) throws Exception {
1329   }
1330 
doPreSymmetry()1331   protected void doPreSymmetry() throws Exception {
1332   }
1333 
1334   @SuppressWarnings("unchecked")
finalizeMOData(Map<String, Object> moData)1335   public void finalizeMOData(Map<String, Object> moData) {
1336     asc.setCurrentModelInfo("moData", moData);
1337     if (moData == null)
1338       return;
1339     Lst<Map<String, Object>> orbitals = (Lst<Map<String, Object>>) moData
1340         .get("mos");
1341     if (orbitals != null)
1342       Logger.info(orbitals.size() + " molecular orbitals read in model "
1343           + asc.atomSetCount);
1344   }
1345 
getElementSymbol(int elementNumber)1346   public static String getElementSymbol(int elementNumber) {
1347     return JmolAdapter.getElementSymbol(elementNumber);
1348   }
1349 
1350   /**
1351    * fills an array with a pre-defined number of lines of token data,
1352    * skipping blank lines in the process
1353    *
1354    * @param data
1355    * @param minLineLen TODO
1356    * @throws Exception
1357    */
fillDataBlock(String[][] data, int minLineLen)1358   protected void fillDataBlock(String[][] data, int minLineLen) throws Exception {
1359     int nLines = data.length;
1360     for (int i = 0; i < nLines; i++) {
1361       data[i] = PT.getTokens(discardLinesUntilNonBlank());
1362       if (data[i].length < minLineLen)
1363         --i;
1364     }
1365 
1366   }
1367 
1368   /**
1369    * fills a double[3][3]
1370    *
1371    * @param tokens or null if to read each line for three values (as last 3 on line)
1372    * @param pt initial index; if tokens == null, then negative index is from end of each line
1373    * @return double[3][3]
1374    * @throws Exception
1375    */
fill3x3(String[] tokens, int pt)1376   protected double[][] fill3x3(String[] tokens, int pt) throws Exception {
1377     double[][] a = new double[3][3];
1378     boolean needTokens = (tokens == null);
1379     int pt0 = pt;
1380     for (int i = 0; i < 3; i++) {
1381       if (needTokens || pt >= tokens.length) {
1382         while ((tokens = PT.getTokens(rd())).length < 3){}
1383         pt = (pt0 < 0 ? tokens.length + pt0 : pt0);
1384       }
1385       for (int j = 0; j < 3; j++)
1386         a[i][j] = Double.valueOf(tokens[pt++]).doubleValue();
1387     }
1388     return a;
1389   }
1390 
1391   /**
1392    * fills a float array with string data from a file
1393    * @param s     string data containing floats
1394    * @param width column width or 0 to read tokens
1395    * @param data  result data to be filled
1396    * @return      data
1397    * @throws Exception
1398    */
fillFloatArray(String s, int width, float[] data)1399   protected float[] fillFloatArray(String s, int width, float[] data)
1400       throws Exception {
1401     String[] tokens = new String[0];
1402     int pt = 0;
1403     for (int i = 0; i < data.length; i++) {
1404       while (tokens != null && pt >= tokens.length) {
1405         if (s == null)
1406           s = rd();
1407         if (width == 0) {
1408           tokens = PT.getTokens(s);
1409         } else {
1410           tokens = new String[s.length() / width];
1411           for (int j = 0; j < tokens.length; j++)
1412             tokens[j] = s.substring(j * width, (j + 1) * width);
1413         }
1414         s = null;
1415         pt = 0;
1416       }
1417       if (tokens == null)
1418         break;
1419       data[i] = parseFloatStr(tokens[pt++]);
1420     }
1421     return data;
1422   }
1423 
1424   /**
1425    * Extracts a block of frequency data from a file. This block may be of two
1426    * types -- either X Y Z across a row or each of X Y Z on a separate line.
1427    * Data is presumed to be in fixed FORTRAN-like column format, not
1428    * space-separated columns.
1429    *
1430    * @param iAtom0
1431    *        the first atom to be assigned a frequency
1432    * @param ac
1433    *        the number of atoms to be assigned
1434    * @param modelAtomCount
1435    *        the number of atoms in each model
1436    * @param ignore
1437    *        the frequencies to ignore because the user has selected only certain
1438    *        vibrations to be read or for whatever reason; length serves to set
1439    *        the number of frequencies to be read
1440    * @param isWide
1441    *        when TRUE, this is a table that has X Y Z for each mode within the
1442    *        same row; when FALSE, this is a table that has X Y Z for each mode
1443    *        on a separate line.
1444    * @param col0
1445    *        the column in which data starts
1446    * @param colWidth
1447    *        the width of the data columns
1448    * @param atomIndexes
1449    *        an array either null or indicating exactly which atoms get the
1450    *        frequencies (used by CrystalReader)
1451    * @param minLineLen
1452    *        TODO
1453    * @param data
1454    *        TODO
1455    * @throws Exception
1456    */
fillFrequencyData(int iAtom0, int ac, int modelAtomCount, boolean[] ignore, boolean isWide, int col0, int colWidth, int[] atomIndexes, int minLineLen, String[][] data)1457   protected void fillFrequencyData(int iAtom0, int ac, int modelAtomCount,
1458                                    boolean[] ignore, boolean isWide, int col0,
1459                                    int colWidth, int[] atomIndexes,
1460                                    int minLineLen, String[][] data)
1461       throws Exception {
1462     boolean withSymmetry = (ac != 0 && modelAtomCount != ac && data == null);
1463     if (ac == 0 && atomIndexes != null)
1464       ac = atomIndexes.length;
1465     int nLines = (isWide ? ac : ac * 3);
1466     int nFreq = ignore.length;
1467     if (data == null) {
1468       data = new String[nLines][];
1469       fillDataBlockFixed(data, col0, colWidth, minLineLen);
1470     } else if (!isWide) {
1471       // Gaussian high precision - get atom index at ptNonblank + 1
1472       int ptNonblank = minLineLen;
1473       fillDataBlockFixed(data, col0, colWidth, -ptNonblank);
1474       if (data[0] == null)
1475         return;
1476       iAtom0 += parseIntAt(line, ptNonblank - 5) - 1;
1477     }
1478     for (int i = 0, atomPt = 0; i < nLines; i++, atomPt++) {
1479       String[] values = data[i];
1480       String[] valuesY = (isWide ? null : data[++i]);
1481       String[] valuesZ = (isWide ? null : data[++i]);
1482       int dataPt = values.length - (isWide ? nFreq * 3 : nFreq) - 1;
1483       for (int j = 0, jj = 0; jj < nFreq; jj++) {
1484         ++dataPt;
1485         String x = values[dataPt];
1486         if (x.charAt(0) == ')') // AMPAC reader!
1487           x = x.substring(1);
1488         float vx = parseFloatStr(x);
1489         float vy = parseFloatStr(isWide ? values[++dataPt] : valuesY[dataPt]);
1490         float vz = parseFloatStr(isWide ? values[++dataPt] : valuesZ[dataPt]);
1491         if (ignore[jj])
1492           continue;
1493         int iAtom = (atomIndexes == null ? atomPt : atomIndexes[atomPt]);
1494         if (iAtom < 0)
1495           continue;
1496         iAtom += iAtom0 + modelAtomCount * j++;
1497         if (debugging)
1498           Logger.debug("atom " + iAtom + " vib" + j + ": " + vx + " " + vy
1499               + " " + vz);
1500         asc.addVibrationVectorWithSymmetry(iAtom, vx, vy, vz, withSymmetry);
1501       }
1502     }
1503   }
1504 
1505   /**
1506    * Fills an array with a predefined number of lines of data that is
1507    * arranged in fixed FORTRAN-like column format.
1508    *
1509    * Used exclusively for frequency data
1510    *
1511    * @param data
1512    * @param col0
1513    * @param colWidth
1514    * @param minLineLen or -ptNonblank
1515    * @throws Exception
1516    */
fillDataBlockFixed(String[][] data, int col0, int colWidth, int minLineLen)1517   protected void fillDataBlockFixed(String[][] data, int col0, int colWidth, int minLineLen)
1518       throws Exception {
1519     if (colWidth == 0) {
1520       fillDataBlock(data, minLineLen);
1521       return;
1522     }
1523     int nLines = data.length;
1524     for (int i = 0; i < nLines; i++) {
1525       discardLinesUntilNonBlank();
1526       // neg minLineLen is a nonblank pt
1527       if (minLineLen < 0 && line.charAt(-minLineLen) == ' ') {
1528         data[0] = null;
1529         return;
1530       }
1531       int nFields = (line.length() - col0 + 1) / colWidth; // Dmol reader is one short
1532       data[i] = new String[nFields];
1533       for (int j = 0, start = col0; j < nFields; j++, start += colWidth)
1534         data[i][j] = line.substring(start, Math.min(line.length(), start + colWidth));
1535     }
1536   }
1537 
readLines(int nLines)1538   protected String readLines(int nLines) throws Exception {
1539     for (int i = nLines; --i >= 0;)
1540       rd();
1541     return line;
1542   }
1543 
discardLinesUntilStartsWith(String startsWith)1544   public String discardLinesUntilStartsWith(String startsWith)
1545       throws Exception {
1546     while (rd() != null && !line.startsWith(startsWith)) {
1547     }
1548     return line;
1549   }
1550 
discardLinesUntilContains(String containsMatch)1551   public String discardLinesUntilContains(String containsMatch)
1552       throws Exception {
1553     while (rd() != null && line.indexOf(containsMatch) < 0) {
1554     }
1555     return line;
1556   }
1557 
discardLinesUntilContains2(String s1, String s2)1558   public String discardLinesUntilContains2(String s1, String s2)
1559       throws Exception {
1560     while (rd() != null && line.indexOf(s1) < 0 && line.indexOf(s2) < 0) {
1561     }
1562     return line;
1563   }
1564 
discardLinesUntilBlank()1565   public String discardLinesUntilBlank() throws Exception {
1566     while (rd() != null && line.trim().length() != 0) {
1567     }
1568     return line;
1569   }
1570 
discardLinesUntilNonBlank()1571   public String discardLinesUntilNonBlank() throws Exception {
1572     while (rd() != null && line.trim().length() == 0) {
1573     }
1574     return line;
1575   }
1576 
checkLineForScript(String line)1577   protected void checkLineForScript(String line) {
1578     this.line = line;
1579     checkCurrentLineForScript();
1580   }
1581 
checkCurrentLineForScript()1582   public void checkCurrentLineForScript() {
1583     if (line.endsWith("#noautobond")) {
1584       line = line.substring(0, line.lastIndexOf('#')).trim();
1585       asc.setNoAutoBond();
1586     }
1587     int pt = line.indexOf("jmolscript:");
1588     if (pt >= 0) {
1589       String script = line.substring(pt + 11, line.length());
1590       if (script.indexOf("#") >= 0) {
1591         script = script.substring(0, script.indexOf("#"));
1592       }
1593       addJmolScript(script);
1594       line = line.substring(0, pt).trim();
1595     }
1596   }
1597 
1598   private String previousScript;
1599 
addJmolScript(String script)1600   public void addJmolScript(String script) {
1601     Logger.info("#jmolScript: " + script);
1602     if (previousScript == null)
1603       previousScript = "";
1604     else if (!previousScript.endsWith(";"))
1605       previousScript += ";";
1606     previousScript += script;
1607     asc.setInfo("jmolscript",
1608         previousScript);
1609   }
1610 
1611   private String siteScript;
1612 
addSiteScript(String script)1613   protected void addSiteScript(String script) {
1614     if (siteScript == null)
1615       siteScript = "";
1616     else if (!siteScript.endsWith(";"))
1617       siteScript += ";";
1618     siteScript += script;
1619     asc.setInfo("sitescript",
1620         siteScript);  // checked in ScriptEvaluator.load()
1621   }
1622 
rd()1623   public String rd() throws Exception {
1624     return RL();
1625   }
1626 
RL()1627   public String RL() throws Exception {
1628     prevline = line;
1629     line = reader.readLine();
1630     if (out != null && line != null)
1631       out.append(line).append("\n");
1632     ptLine++;
1633     if (debugging && line != null)
1634       Logger.info(line);
1635     return line;
1636   }
1637 
getStrings(String sinfo, int nFields, int width)1638   final static protected String[] getStrings(String sinfo, int nFields,
1639                                              int width) {
1640     String[] fields = new String[nFields];
1641     for (int i = 0, pt = 0; i < nFields; i++, pt += width)
1642       fields[i] = sinfo.substring(pt, pt + width);
1643     return fields;
1644   }
1645 
1646   // parser functions are static, so they need notstatic counterparts
1647 
getTokens()1648   public String[] getTokens() {
1649     return PT.getTokens(line);
1650   }
1651 
getTokensFloat(String s, float[] f, int n)1652   public static float[] getTokensFloat(String s, float[] f, int n) {
1653     if (f == null)
1654       f = new float[n];
1655     PT.parseFloatArrayDataN(PT.getTokens(s), f, n);
1656     return f;
1657   }
1658 
parseFloat()1659   protected float parseFloat() {
1660     return PT.parseFloatNext(line, next);
1661   }
1662 
parseFloatStr(String s)1663   public float parseFloatStr(String s) {
1664     next[0] = 0;
1665     return PT.parseFloatNext(s, next);
1666   }
1667 
parseFloatRange(String s, int iStart, int iEnd)1668   protected float parseFloatRange(String s, int iStart, int iEnd) {
1669     next[0] = iStart;
1670     return PT.parseFloatRange(s, iEnd, next);
1671   }
1672 
parseInt()1673   protected int parseInt() {
1674     return PT.parseIntNext(line, next);
1675   }
1676 
parseIntStr(String s)1677   public int parseIntStr(String s) {
1678     next[0] = 0;
1679     return PT.parseIntNext(s, next);
1680   }
1681 
parseIntAt(String s, int iStart)1682   public int parseIntAt(String s, int iStart) {
1683     next[0] = iStart;
1684     return PT.parseIntNext(s, next);
1685   }
1686 
parseIntRange(String s, int iStart, int iEnd)1687   protected int parseIntRange(String s, int iStart, int iEnd) {
1688     next[0] = iStart;
1689     return PT.parseIntRange(s, iEnd, next);
1690   }
1691 
parseToken()1692   protected String parseToken() {
1693     return PT.parseTokenNext(line, next);
1694   }
1695 
parseTokenStr(String s)1696   protected String parseTokenStr(String s) {
1697     next[0] = 0;
1698     return PT.parseTokenNext(s, next);
1699   }
1700 
parseTokenNext(String s)1701   protected String parseTokenNext(String s) {
1702     return PT.parseTokenNext(s, next);
1703   }
1704 
parseTokenRange(String s, int iStart, int iEnd)1705   protected String parseTokenRange(String s, int iStart, int iEnd) {
1706     next[0] = iStart;
1707     return PT.parseTokenRange(s, iEnd, next);
1708   }
1709 
1710   /**
1711    * get all integers after letters
1712    * negative entries are spaces (1Xn)
1713    *
1714    * @param s
1715    * @return Vector of integers
1716    */
getFortranFormatLengths(String s)1717   protected static Lst<Integer> getFortranFormatLengths(String s) {
1718     Lst<Integer> vdata = new  Lst<Integer>();
1719     int n = 0;
1720     int c = 0;
1721     int factor = 1;
1722     boolean inN = false;
1723     boolean inCount = true;
1724     s += ",";
1725     for (int i = 0; i < s.length(); i++) {
1726       char ch = s.charAt(i);
1727       switch (ch) {
1728       case '.':
1729         inN = false;
1730         continue;
1731       case ',':
1732         for (int j = 0; j < c; j++)
1733           vdata.addLast(Integer.valueOf(n * factor));
1734         inN = false;
1735         inCount = true;
1736         c = 0;
1737         continue;
1738       case 'X':
1739         n = c;
1740         c = 1;
1741         factor = -1;
1742         continue;
1743 
1744       }
1745       boolean isDigit = PT.isDigit(ch);
1746       if (isDigit) {
1747         if (inN)
1748           n = n * 10 + ch - '0';
1749         else if (inCount)
1750           c = c * 10 + ch - '0';
1751       } else if (PT.isLetter(ch)) {
1752         n = 0;
1753         inN = true;
1754         inCount = false;
1755         factor = 1;
1756       } else {
1757         inN = false;
1758       }
1759     }
1760     return vdata;
1761   }
1762 
1763   /**
1764    * read three vectors, as for unit cube definitions
1765    * allows for non-numeric data preceding the number block
1766    *
1767    * @param isBohr
1768    * @return three vectors
1769    * @throws Exception
1770    *
1771    */
read3Vectors(boolean isBohr)1772   protected V3[] read3Vectors(boolean isBohr) throws Exception {
1773     V3[] vectors = new V3[3];
1774     float[] f = new float[3];
1775     for (int i = 0; i < 3; i++) {
1776       if (i > 0 || Float.isNaN(parseFloatStr(line))) {
1777         rd();
1778         if (i == 0 && line != null) {
1779           i = -1;
1780           continue;
1781         }
1782       }
1783       fillFloatArray(line, 0, f);
1784       vectors[i] = new V3();
1785       vectors[i].setA(f);
1786       if (isBohr)
1787         vectors[i].scale(ANGSTROMS_PER_BOHR);
1788     }
1789     return vectors;
1790   }
1791 
1792   /**
1793    * allow 13C, 15N, 2H, etc. for isotopes
1794    *
1795    * @param atom
1796    * @param str
1797    */
setElementAndIsotope(Atom atom, String str)1798   protected void setElementAndIsotope(Atom atom, String str) {
1799     int isotope = parseIntStr(str);
1800     if (isotope == Integer.MIN_VALUE) {
1801       atom.elementSymbol = str;
1802     } else {
1803       str = str.substring(("" + isotope).length());
1804       atom.elementNumber = (short) (str.length() == 0 ? isotope
1805           : ((isotope << 7) + JmolAdapter.getElementNumber(str)));
1806     }
1807   }
1808 
finalizeModelSet()1809   public void finalizeModelSet() {
1810     // PyMOL reader only
1811   }
1812 
setChainID(Atom atom, String label)1813   public void setChainID(Atom atom, String label) {
1814     atom.chainID = vwr.getChainID(label, true);
1815   }
1816 
1817   @Override
readNextLine()1818   public String readNextLine() throws Exception {
1819     // from CifDataReader, DSSRDataReader
1820     if (rd() != null && line.indexOf("#jmolscript:") >= 0)
1821       checkCurrentLineForScript();
1822     return line;
1823   }
1824 
appendUunitCellInfo(String info)1825   public void appendUunitCellInfo(String info) {
1826     if (moreUnitCellInfo == null)
1827       moreUnitCellInfo = new Lst<String>();
1828     moreUnitCellInfo.addLast(info);
1829     appendLoadNote(info);
1830   }
1831 
getInterface(String className)1832   public Object getInterface(String className) {
1833     Object o = Interface.getInterface(className, vwr, "file");
1834     if (o == null)
1835       throw new NullPointerException("Interface");
1836     return o;
1837 }
1838 
forceSymmetry(boolean andPack)1839   public void forceSymmetry(boolean andPack) {
1840     if (andPack)
1841       doPackUnitCell = andPack;
1842     if (!doApplySymmetry) {
1843       doApplySymmetry = true;
1844       latticeCells[0] = 1;
1845       latticeCells[1] = 1;
1846       latticeCells[2] = 1;
1847     }
1848   }
1849 
1850 }
1851