1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2007-10-14 12:33:20 -0500 (Sun, 14 Oct 2007) $
4  * $Revision: 8408 $
5 
6  *
7  * Copyright (C) 2003-2005  The Jmol Development Team
8  *
9  * Contact: jmol-developers@lists.sf.net
10  *
11  *  This library is free software; you can redistribute it and/or
12  *  modify it under the terms of the GNU Lesser General Public
13  *  License as published by the Free Software Foundation; either
14  *  version 2.1 of the License, or (at your option) any later version.
15  *
16  *  This library is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  *  Lesser General Public License for more details.
20  *
21  *  You should have received a copy of the GNU Lesser General Public
22  *  License along with this library; if not, write to the Free Software
23  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25 
26 package org.jmol.modelset;
27 
28 import java.util.Hashtable;
29 import java.util.Map;
30 
31 
32 
33 import javajs.util.AU;
34 import javajs.util.Lst;
35 import javajs.util.P3;
36 import javajs.util.PT;
37 import javajs.util.SB;
38 import javajs.util.T3;
39 
40 import org.jmol.api.JmolDataManager;
41 import org.jmol.script.SV;
42 import org.jmol.script.T;
43 import org.jmol.util.Edge;
44 import org.jmol.viewer.JC;
45 import org.jmol.viewer.Viewer;
46 
47 
48 public class LabelToken {
49 
50   /*
51    * by Bob Hanson, 5/28/2009
52    *
53    * a compiler for the atom label business.
54    *
55    * Prior to this, once for every atom, twice for every bond, and 2-4 times for every
56    * measurement we were scanning the format character by character. And if data were
57    * involved, then calls were made for every atom to find the data set and return its
58    * value. Now you can still do that, but the Jmol code doesn't.
59    *
60    * Instead, we now first compile a set of tokens -- either pure text or some
61    * sort of %xxxx business. Generally we would alternate between these, so the
62    * compiler is set up to initialize an array that has 2n+1 elements, where n is the
63    * number of % signs in the string. This is guaranteed to be more than really necessary.
64    *
65    * Because we are working with tokens, we can go beyond the limiting A-Za-z business
66    * that we had before. That still works, but now we can have any standard token be
67    * used in brackets:
68    *
69    *   %n.m[xxxxx]
70    *
71    * This complements the
72    *
73    *   %n.m{xxxxx}
74    *
75    * used for data. The brackets make for a nice-looking format:
76    *
77    *
78    *  print {*}.bonds.label("%6[atomName]1 - %6[atomName]2  %3ORDER  %6.2LENGTH")
79    *
80    * [Note that the %ORDER and %LENGTH variables are bond labeling options, and
81    *  the 1 and 2 after %[xxx] indicate which atom in involved.
82    *
83    *
84    */
85 
86   private String text;
87   private String key;
88   private Object data;
89   private int tok;
90   private int pt = -1;
91   private char ch1 = '\0';
92   private int width;
93   private int precision = Integer.MAX_VALUE;
94   private boolean alignLeft;
95   private boolean zeroPad;
96   private boolean intAsFloat;
97 
98   // do not change array order without changing string order as well
99   // new tokens can be added to the list at the end
100   // and then also added in appendTokenValue()
101   // and also in Eval, to atomProperty()
102 
103   final private static String labelTokenParams = "AaBbCcDEefGgIiLlMmNnOoPpQqRrSsTtUuVvWXxxYyyZzz%%%gqW";
104   final private static int[] labelTokenIds = {
105       /* 'A' */T.altloc,
106       /* 'a' */T.atomname,
107       /* 'B' */T.atomtype,
108       /* 'b' */T.temperature,
109       /* 'C' */T.formalcharge,
110       /* 'c' */T.chain,
111       /* 'D' */T.atomindex,
112       /* 'E' */T.insertion,
113       /* 'e' */T.element,
114       /* 'f' */T.phi,
115       /* 'G' */T.groupindex,
116       /* 'g' */T.monomer, //getSelectedGroupIndexWithinChain()
117       /* 'I' */T.bondingradius,
118       /* 'i' */T.atomno,
119       /* 'L' */T.polymerlength,
120       /* 'l' */T.elemno,
121       /* 'M' */T.model,
122       /* 'm' */T.group1,
123       /* 'N' */T.molecule,
124       /* 'n' */T.group,
125       /* 'O' */79, // all symmetry operators
126       /* 'o' */T.symmetry,
127       /* 'P' */T.partialcharge,
128       /* 'p' */T.psi,
129       /* 'Q' */81, //occupancy 0.0 to 1.0
130       /* 'q' */T.occupancy,
131       /* 'R' */T.resno,
132       /* 'r' */T.seqcode,
133       /* 'S' */T.site,
134       /* 's' */T.chain,
135       /* 'T' */T.straightness,
136       /* 't' */T.temperature,
137       /* 'U' */T.identify,
138       /* 'u' */T.surfacedistance,
139       /* 'V' */T.vanderwaals,
140       /* 'v' */T.vibxyz,
141       /* 'W' */T.w, // identifier and XYZ coord
142       /* 'X' */T.fracx,
143       /* 'x' */T.atomx,
144       /* 'x' */T.x,
145       /* 'Y' */T.fracy,
146       /* 'y' */T.atomy,
147       /* 'y' */T.y,
148       /* 'Z' */T.fracz,
149       /* 'z' */T.atomz,
150       /* 'z' */T.z,
151 
152       // not having letter equivalents:
153 
154       //new for Jmol 11.9.5:
155       T.backbone, T.cartoon, T.dots, T.ellipsoid,
156       T.geosurface, T.halo, T.meshRibbon, T.ribbon,
157       T.rocket, T.star, T.strands, T.trace,
158 
159       T.adpmax, T.adpmin, T.atomid, T.bondcount, T.color,
160       T.groupid, T.covalentradius, T.file, T.format, T.label,
161       T.mass, T.modelindex, T.eta, T.omega, T.polymer, T.property,
162       T.radius, T.selected, T.shape, T.sequence,
163       T.spacefill, T.structure, T.substructure, T.strucno,
164       T.strucid, T.symbol, T.theta, T.unitx, T.unity,
165       T.unitz, T.valence, T.vectorscale, T.vibx, T.viby, T.vibz,
166       T.volume, T.unitxyz, T.fracxyz, T.xyz, T.fuxyz,
167       T.fux, T.fuy, T.fuz, T.hydrophobicity, T.screenx,
168       T.screeny, T.screenz, T.screenxyz, // added in 12.3.30
169       T.magneticshielding, T.chemicalshift, T.chainno, T.seqid,
170       T.modx, T.mody, T.modz, T.modo, T.modxyz, T.symop,
171       T.nbo, // added in 14.8.2
172       T.chirality, // added in 14.11.4
173       T.ciprule // added in 14.17.0
174   };
175 
LabelToken()176   public LabelToken() {
177     // for access to static methods without preloading JavaScript
178   }
set(String text, int pt)179   private LabelToken set(String text, int pt) {
180     this.text = text;
181     this.pt = pt;
182     return this;
183   }
184 
isLabelPropertyTok(int tok)185   private static boolean isLabelPropertyTok(int tok) {
186     for (int i = labelTokenIds.length; --i >= 0;)
187       if (labelTokenIds[i] == tok)
188         return true;
189     return false;
190   }
191 
192   public static final String STANDARD_LABEL = "%[identify]";
193 
194   private final static String twoCharLabelTokenParams = "fuv";
195 
196   private final static int[] twoCharLabelTokenIds = { T.fracx, T.fracy,
197       T.fracz, T.unitx, T.unity, T.unitz, T.vibx,
198       T.viby, T.vibz, };
199 
200   /**
201    * Compiles a set of tokens for each primitive element of a
202    * label. This is the efficient way to create a set of labels.
203    *
204    * @param vwr
205    * @param strFormat
206    * @param chAtom
207    * @param htValues
208    * @return   array of tokens
209    */
compile(Viewer vwr, String strFormat, char chAtom, Map<String, Object> htValues)210   public static LabelToken[] compile(Viewer vwr, String strFormat,
211                                      char chAtom, Map<String, Object> htValues) {
212     if (strFormat == null || strFormat.length() == 0)
213       return null;
214     if (strFormat.indexOf("%") < 0 || strFormat.length() < 2)
215       return new LabelToken[] { new LabelToken().set(strFormat, -1) };
216     int n = 0;
217     int ich = -1;
218     int cch = strFormat.length();
219     while (++ich < cch && (ich = strFormat.indexOf('%', ich)) >= 0)
220       n++;
221     LabelToken[] tokens = new LabelToken[n * 2 + 1];
222     int ichPercent;
223     int i = 0;
224     for (ich = 0; (ichPercent = strFormat.indexOf('%', ich)) >= 0;) {
225       if (ich != ichPercent)
226         tokens[i++] = new LabelToken().set(strFormat.substring(ich, ichPercent), -1);
227       LabelToken lt = tokens[i++] = new LabelToken().set(null, ichPercent);
228       vwr.autoCalculate(lt.tok, null);
229       ich = setToken(vwr, strFormat, lt, cch, chAtom, htValues);
230     }
231     if (ich < cch)
232       tokens[i++] = new LabelToken().set(strFormat.substring(ich), -1);
233     return tokens;
234   }
235 
236   //////////// label formatting for atoms, bonds, and measurements ///////////
237 
formatLabel(Viewer vwr, Atom atom, String strFormat, P3 ptTemp)238   public String formatLabel(Viewer vwr, Atom atom, String strFormat, P3 ptTemp) {
239     return (strFormat == null || strFormat.length() == 0 ? null
240         : formatLabelAtomArray(vwr, atom, compile(vwr, strFormat, '\0', null),
241             '\0', null, ptTemp));
242   }
243 
244   /**
245    * returns a formatted string based on the precompiled label tokens
246    *
247    * @param vwr
248    * @param atom
249    * @param tokens
250    * @param chAtom
251    * @param indices
252    * @param ptTemp
253    * @return   formatted string
254    */
formatLabelAtomArray(Viewer vwr, Atom atom, LabelToken[] tokens, char chAtom, int[] indices, P3 ptTemp)255   public static String formatLabelAtomArray(Viewer vwr, Atom atom,
256                                    LabelToken[] tokens, char chAtom,
257                                    int[] indices, P3 ptTemp) {
258     if (atom == null)
259       return null;
260     SB strLabel = (chAtom > '0' ? null : new SB());
261     if (tokens != null)
262       for (int i = 0; i < tokens.length; i++) {
263         LabelToken t = tokens[i];
264         if (t == null)
265           break;
266         if (chAtom > '0' && t.ch1 != chAtom)
267           continue;
268         if (t.tok <= 0 || t.key != null) {
269           if (strLabel != null) {
270             strLabel.append(t.text);
271             if (t.ch1 != '\0')
272               strLabel.appendC(t.ch1);
273           }
274         } else {
275           appendAtomTokenValue(vwr, atom, t, strLabel, indices, ptTemp);
276         }
277       }
278     return (strLabel == null ? null : strLabel.toString().intern());
279   }
280 
getBondLabelValues()281   public static Map<String, Object> getBondLabelValues() {
282     Map<String, Object> htValues = new Hashtable<String, Object>();
283     htValues.put("#", "");
284     htValues.put("ORDER", "");
285     htValues.put("TYPE", "");
286     htValues.put("LENGTH", Float.valueOf(0));
287     htValues.put("ENERGY", Float.valueOf(0));
288     return htValues;
289   }
290 
formatLabelBond(Viewer vwr, Bond bond, LabelToken[] tokens, Map<String, Object> values, int[] indices, P3 ptTemp)291   public static String formatLabelBond(Viewer vwr, Bond bond,
292                                    LabelToken[] tokens,
293                                    Map<String, Object> values, int[] indices, P3 ptTemp) {
294     values.put("#", "" + (bond.index + 1));
295     values.put("ORDER", "" + Edge.getBondOrderNumberFromOrder(bond.order));
296     values.put("TYPE", Edge.getBondOrderNameFromOrder(bond.order));
297     values.put("LENGTH", Float.valueOf(bond.atom1.distance(bond.atom2)));
298     values.put("ENERGY", Float.valueOf(bond.getEnergy()));
299     setValues(tokens, values);
300     formatLabelAtomArray(vwr, bond.atom1, tokens, '1', indices, ptTemp);
301     formatLabelAtomArray(vwr, bond.atom2, tokens, '2', indices, ptTemp);
302     return getLabel(tokens);
303   }
304 
formatLabelMeasure(Viewer vwr, Measurement m, String label, float value, String units)305   public static String formatLabelMeasure(Viewer vwr, Measurement m,
306                                    String label, float value, String units) {
307     Map<String, Object> htValues = new Hashtable<String, Object>();
308     htValues.put("#", "" + (m.index + 1));
309     htValues.put("VALUE", Float.valueOf(value));
310     htValues.put("UNITS", units);
311     LabelToken[] tokens = compile(vwr, label, '\1', htValues);
312     if (tokens == null)
313       return "";
314     setValues(tokens, htValues);
315     Atom[] atoms = m.ms.at;
316     int[] indices = m.countPlusIndices;
317     for (int i = indices[0]; i >= 1; --i)
318       if (indices[i] >= 0)
319         formatLabelAtomArray(vwr, atoms[indices[i]], tokens, (char) ('0' + i), null, null);
320     label = getLabel(tokens);
321     return (label == null ? "" : label);
322   }
323 
setValues(LabelToken[] tokens, Map<String, Object> values)324   public static void setValues(LabelToken[] tokens, Map<String, Object> values) {
325     for (int i = 0; i < tokens.length; i++) {
326       LabelToken lt = tokens[i];
327       if (lt == null)
328         break;
329       if (lt.key == null)
330         continue;
331       Object value = values.get(lt.key);
332       lt.text = (value instanceof Float ? lt.format(((Float) value)
333           .floatValue(), null, null) : lt.format(Float.NaN, (String) value,
334           null));
335     }
336   }
337 
getLabel(LabelToken[] tokens)338   public static String getLabel(LabelToken[] tokens) {
339     SB sb = new SB();
340     for (int i = 0; i < tokens.length; i++) {
341       LabelToken lt = tokens[i];
342       if (lt == null)
343         break;
344       sb.append(lt.text);
345     }
346     return sb.toString();
347   }
348 
349   /////////////////// private methods
350 
351   /**
352    * sets a label token based on a label string
353    *
354    * @param vwr
355    * @param strFormat
356    * @param lt
357    * @param cch
358    * @param chAtom
359    * @param htValues
360    * @return         new position
361    */
setToken(Viewer vwr, String strFormat, LabelToken lt, int cch, int chAtom, Map<String, Object> htValues)362   private static int setToken(Viewer vwr, String strFormat, LabelToken lt,
363                               int cch, int chAtom, Map<String, Object> htValues) {
364     int ich = lt.pt + 1;
365     // trailing % is OK
366     if (ich >= cch) {
367       lt.text = "%";
368       return ich;
369     }
370     char ch;
371     if (strFormat.charAt(ich) == '-') {
372       lt.alignLeft = true;
373       ++ich;
374     }
375     if (ich < cch && strFormat.charAt(ich) == '0') {
376       lt.zeroPad = true;
377       ++ich;
378     }
379     while (ich < cch && PT.isDigit(ch = strFormat.charAt(ich))) {
380       lt.width = (10 * lt.width) + (ch - '0');
381       ++ich;
382     }
383     lt.precision = Integer.MAX_VALUE;
384     boolean isNegative = false;
385     if (ich < cch && strFormat.charAt(ich) == '.') {
386       ++ich;
387       if (ich < cch && (ch = strFormat.charAt(ich)) == '-') {
388         isNegative = true;
389         ++ich;
390       }
391       if (ich < cch && PT.isDigit(ch = strFormat.charAt(ich))) {
392         lt.precision = ch - '0';
393         if (isNegative)
394           lt.precision = -1 - lt.precision;
395         ++ich;
396       }
397     }
398     if (ich < cch && htValues != null)
399       for (String key: htValues.keySet())
400         if (strFormat.indexOf(key) == ich)
401           return ich + (lt.key = key).length();
402     if (ich < cch)
403       switch (ch = strFormat.charAt(ich++)) {
404       case '%':
405         lt.text = "%";
406         return ich;
407       case '[':
408         int ichClose = strFormat.indexOf(']', ich);
409         if (ichClose < ich) {
410           ich = cch;
411           break;
412         }
413         String propertyName = strFormat.substring(ich, ichClose).toLowerCase();
414         if (propertyName.startsWith("property_")) {
415           lt.tok = T.data;
416           lt.data = vwr.getDataObj(propertyName, null, JmolDataManager.DATA_TYPE_AF);
417         } else if (propertyName.startsWith("validation.")) {
418           lt.tok = T.validation;
419           lt.data = vwr.getDataObj("property_" + propertyName.substring(11), null, JmolDataManager.DATA_TYPE_AF);
420         } else if (propertyName.startsWith("unitid")) {
421            lt.tok = T.id;
422            lt.data = Integer.valueOf(JC.getUnitIDFlags(propertyName.substring(6)));
423         } else {
424           T token = T.getTokenFromName(propertyName);
425           if (token != null && isLabelPropertyTok(token.tok))
426             lt.tok = token.tok;
427         }
428         ich = ichClose + 1;
429         break;
430       case '{':
431         // label %{altName}
432         // client property name deprecated in 12.1.22
433         // but this can be passed to Jmol from the reader
434         // as an auxiliaryInfo array or '\n'-delimited string
435         int ichCloseBracket = strFormat.indexOf('}', ich);
436         if (ichCloseBracket < ich) {
437           ich = cch;
438           break;
439         }
440         String s = strFormat.substring(ich, ichCloseBracket);
441         lt.data = vwr.getDataObj(s, null, JmolDataManager.DATA_TYPE_AF);
442         // TODO untested j2s issue fix
443         if (lt.data == null) {
444           lt.data = vwr.getDataObj(s, null, JmolDataManager.DATA_TYPE_UNKNOWN);
445           if (lt.data != null) {
446             lt.data = ((Object[]) lt.data)[1];
447             if (lt.data instanceof String)
448               lt.data = PT.split((String) lt.data, "\n");
449             if (!(AU.isAS(lt.data)))
450               lt.data = null;
451           }
452           if (lt.data == null) {
453             lt.tok = T.property;
454             lt.data = s;
455           } else {
456             lt.tok = T.array;
457           }
458         } else {
459           lt.tok = T.data;
460         }
461         ich = ichCloseBracket + 1;
462         break;
463       default:
464         int i,
465         i1;
466         if (ich < cch && (i = twoCharLabelTokenParams.indexOf(ch)) >= 0
467             && (i1 = "xyz".indexOf(strFormat.charAt(ich))) >= 0) {
468           lt.tok = twoCharLabelTokenIds[i * 3 + i1];
469           ich++;
470         } else if ((i = labelTokenParams.indexOf(ch)) >= 0) {
471           lt.tok = labelTokenIds[i];
472         }
473       }
474     lt.text = strFormat.substring(lt.pt, ich);
475     if (ich < cch && chAtom != '\0'
476         && PT.isDigit(ch = strFormat.charAt(ich))) {
477       ich++;
478       lt.ch1 = ch;
479       if (ch != chAtom && chAtom != '\1')
480         lt.tok = 0;
481     }
482     return ich;
483   }
484 
appendAtomTokenValue(Viewer vwr, Atom atom, LabelToken t, SB strLabel, int[] indices, P3 ptTemp)485   private static void appendAtomTokenValue(Viewer vwr, Atom atom, LabelToken t,
486                                            SB strLabel, int[] indices, P3 ptTemp) {
487     String strT = null;
488     float floatT = Float.NaN;
489     T3 ptT = null;
490     try {
491       switch (t.tok) {
492 
493       // special cases only for labels
494 
495       case T.atomindex:
496         strT = "" + (indices == null ? atom.i : indices[atom.i]);
497         break;
498       case T.color:
499         ptT = atom.atomPropertyTuple(vwr, t.tok, ptTemp);
500         break;
501       case T.id:
502         strT = atom.getUnitID(((Integer) t.data).intValue());
503         break;
504       case T.data:
505       case T.validation:
506         if (t.data != null) {
507           floatT = ((float[]) t.data)[atom.i];
508           if (t.tok == T.validation && floatT != 1 && floatT != 0) {
509             Lst<Float> o = vwr.getAtomValidation(
510                 t.text.substring(13, t.text.length() - 1), atom);
511             if (o == null) {
512               System.out.println("?? o is null ??");
513             } else if (o.size() == 1) {
514               floatT = o.get(0).floatValue();
515             } else {
516               floatT = Float.NaN;
517               strT = "";
518               for (int i = 0, n = o.size(); i < n; i++) {
519                 strT += "," + o.get(i);
520               }
521               if (strT.length() > 1)
522                 strT = strT.substring(1);
523             }
524           }
525         }
526         break;
527       case T.property:
528         // label %{altName}
529         Object data = vwr.ms.getInfo(atom.mi, (String) t.data);
530         int iatom = atom.i - vwr.ms.am[atom.mi].firstAtomIndex;
531         Object o = null;
532         if (iatom >= 0)
533           if ((data instanceof Object[])) {
534             // unlikely that this will be the case.
535             // it would have to be
536             Object[] sdata = (Object[]) data;
537             o = (iatom < sdata.length ? sdata[iatom] : null);
538           } else if (data instanceof Lst<?>) {
539             @SuppressWarnings("unchecked")
540             Lst<SV> list = (Lst<SV>) data;
541             o = (iatom < list.size() ? SV.oValue(list.get(iatom)) : null);
542           }
543         if (o == null) {
544           strT = "";
545         } else if (o instanceof Float) {
546           floatT = ((Float) o).floatValue();
547         } else if (o instanceof Integer) {
548           floatT = ((Integer) o).intValue();
549         } else if (o instanceof T3) {
550           ptT = (T3) o;
551         } else {
552           strT = o.toString();
553         }
554         break;
555       case T.array:
556         if (t.data != null) {
557           String[] sdata = (String[]) t.data;
558           strT = (atom.i < sdata.length ? sdata[atom.i] : "");
559         }
560         break;
561       case T.formalcharge:
562         int formalCharge = atom.getFormalCharge();
563         strT = (formalCharge > 0 ? "" + formalCharge + "+"
564             : formalCharge < 0 ? "" + -formalCharge + "-" : "");
565         break;
566       case T.model:
567         strT = atom.getModelNumberForLabel();
568         break;
569       case T.occupancy:
570         strT = "" + atom.atomPropertyInt(t.tok);
571         break;
572       case T.radius:
573         floatT = atom.atomPropertyFloat(vwr, t.tok, ptTemp);
574         break;
575       case T.strucid:
576         strT = atom.group.getStructureId();
577         break;
578       case T.strucno:
579         int id = atom.group.getStrucNo();
580         strT = (id <= 0 ? "" : "" + id);
581         break;
582       case T.straightness:
583         if (Float.isNaN(floatT = atom.group.getGroupParameter(T.straightness)))
584           strT = "null";
585         break;
586       case T.vibx:
587       case T.viby:
588       case T.vibz:
589       case T.modx:
590       case T.mody:
591       case T.modz:
592       case T.modo:
593         floatT = atom.atomPropertyFloat(vwr, t.tok, ptTemp);
594         if (Float.isNaN(floatT))
595           strT = "";
596         break;
597       case T.nbo:
598         strT = vwr.getNBOAtomLabel(atom);
599         break;
600       case T.seqcode: // see 1h4w  184^A
601       case T.structure:
602       case T.substructure:
603         strT = atom.atomPropertyString(vwr, t.tok);
604         break;
605       case T.w:
606         strT = atom.getIdentityXYZ(false, ptTemp);
607         break;
608 
609       // characters only
610       // JavaScript switch cannot handle mixed cases of numbers and character codes
611       case 79://'O':
612         strT = atom.getSymmetryOperatorList(false);
613         break;
614       case 81://'Q':
615         floatT = atom.getOccupancy100() / 100f;
616         break;
617 
618       // standard
619 
620       default:
621         switch (t.tok & T.PROPERTYFLAGS) {
622         case T.intproperty:
623           if (t.intAsFloat)
624             floatT = atom.atomPropertyInt(t.tok);
625           else
626             strT = "" + atom.atomPropertyInt(t.tok);
627           break;
628         case T.floatproperty:
629           floatT = atom.atomPropertyFloat(vwr, t.tok, ptTemp);
630           break;
631         case T.strproperty:
632           strT = atom.atomPropertyString(vwr, t.tok);
633           break;
634         case T.atomproperty:
635           ptT = atom.atomPropertyTuple(vwr, t.tok, ptTemp);
636           if (ptT == null)
637             strT = "";
638           break;
639         default:
640           // any dual case would be here -- must handle specially
641         }
642       }
643     } catch (IndexOutOfBoundsException ioobe) {
644       floatT = Float.NaN;
645       strT = null;
646       ptT = null;
647     }
648     strT = t.format(floatT, strT, ptT);
649     if (strLabel == null)
650       t.text = strT;
651     else
652       strLabel.append(strT);
653   }
654 
format(float floatT, String strT, T3 ptT)655   private String format(float floatT, String strT, T3 ptT) {
656     if (!Float.isNaN(floatT)) {
657       return PT.formatF(floatT, width, precision, alignLeft, zeroPad);
658     } else if (strT != null) {
659       return PT.formatS(strT, width, precision, alignLeft, zeroPad);
660     } else if (ptT != null) {
661       if (width == 0 && precision == Integer.MAX_VALUE) {
662         width = 6;
663         precision = 2;
664       }
665       return PT.formatF(ptT.x, width, precision, false, false)
666           + PT.formatF(ptT.y, width, precision, false, false)
667           + PT.formatF(ptT.z, width, precision, false, false);
668     } else {
669       return text;
670     }
671   }
672 
673 }
674