001/*
002*   GlobalSection  -- Encapsulates the IGES Global Section.
003*
004*   Copyright (C) 2010-2016, Joseph A. Huwaldt.
005*   All rights reserved.
006*   
007*   This library is free software; you can redistribute it and/or
008*   modify it under the terms of the GNU Lesser General Public
009*   License as published by the Free Software Foundation; either
010*   version 2.1 of the License, or (at your option) any later version.
011*   
012*   This library is distributed in the hope that it will be useful,
013*   but WITHOUT ANY WARRANTY; without even the implied warranty of
014*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015*   Lesser General Public License for more details.
016*
017*   You should have received a copy of the GNU Lesser General Public License
018*   along with this program; if not, write to the Free Software
019*   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
020*   Or visit:  http://www.gnu.org/licenses/lgpl.html
021*
022*   Based on, but heavily modified from, IGESView ( http://ts.nist.gov/Standards/IGES/igesTools.cfm )
023*/
024package geomss.geom.reader.iges;
025
026import jahuwaldt.js.param.Parameter;
027import java.io.IOException;
028import java.io.PrintWriter;
029import java.io.RandomAccessFile;
030import java.text.SimpleDateFormat;
031import java.util.Date;
032import javax.measure.quantity.Length;
033import javax.measure.unit.NonSI;
034import javax.measure.unit.SI;
035import javax.measure.unit.Unit;
036
037/**
038 * The GlobalSection class encapsulates the IGES Global Section.  This holds
039 * the "global variables" for the file.  This class handles initialization,
040 * reading, access, and dumping of the values.
041 *
042 *  <p>  Modified by:  Joseph A. Huwaldt </p>
043 *
044 * @author JDN, Version 1.0
045 * @version April 7, 2016
046 */
047public class GlobalSection {
048    
049    private char   Delim = ',';         // Parameter delimiter character
050    private char   Term = ';';          // Record delimiter character
051    private String PName = "";          // Product identification from sending system
052    private String FName = "";          // File name
053    private String SysID = Constants.RESOURCES.getString("systemID");       // System ID
054    private String TranVrsn = Constants.RESOURCES.getString("tranVersion"); // Preprocessor version
055    private int    IntSize = 32;        // Number of binary bits for integer representation
056    private int    FloatExp = 38;       // Max power of ten for float
057    private int    FloatMant = 6;       // Number of significant digits for float
058    private int    DoubleExp = 308;     // Max power of ten for double
059    private int    DoubleMant = 15;     // Number of significant digits for double
060    private String DestName = "";       // Product identification for the receiving system
061    private double ModelScale = 1;      // Model space scale
062    private int    UnitFlg = 1;         // Unit flag (unit code)
063    private String UnitType = "IN";     // Units name
064    private int    LineWeights = 1;     // Max number of line weight gradations
065    private double LineWidth = 0.01;    // Width of max line weight in units
066    private String DateTime = "";       // Date and time of file generation
067    private double Grain = 0.01;        // Min granularity of model in units
068    private double MaxValue = 0;        // Max coordinate value in units
069    private String Author = "";         // Name of author
070    private String Organization = "";   // Author's organization
071    private int    SpecVrsn = 11;       // Version of specification for this file
072    private int    DraftStd = 0;        // Drafting standard of this file
073    private String ModDate = "";        // Date and time of last modification to the model.
074    private String AppProtocol = "";    // Application protocol, application subset, MIL-spec, etc.
075    
076    private int    start = 0;           // Current string index for parsing
077    private String GlobalText = "";     // Global data as a string
078
079    /**
080     * Default constructor.
081     */
082    public GlobalSection() {
083    }
084
085    /**
086     * Return char parameter from input string. The input string must be in Hollerith form
087     * (1H-), as per the IGES specification, and delimited by the global delimiter
088     * character.
089     *
090     * @param s input string from IGES file.
091     * @return single character
092     */
093    private char getChar(String s) {
094        String sResult = "";
095
096        while (s.charAt(start) == ' ')
097            start++;
098
099        //  Deal with hitting the terminator right away.
100        int c = s.charAt(start);
101        if (c == Delim || c == Term) {
102            if (c == Delim)
103                ++start;        //  Advance only if it is the delimiter that is hit.
104            return '\0';
105        }
106
107        if ((s.charAt(start) != Delim) && (s.charAt(start) != Term) && Character.isDigit(s.charAt(start))) {
108            // Should be in form ##Hchars
109            int sStart = s.indexOf('H', start);
110            String str = s.substring(start, sStart).trim();
111            int iLen = Integer.parseInt(str);
112            int newstart = sStart + 2 + iLen;
113
114            if (newstart < 0)
115                sResult = "";
116            else {
117                sResult = s.substring(sStart + 1, newstart - 1);
118                start = newstart - 1;
119            }
120
121        } else {
122            while ((s.charAt(start) != Delim) && (s.charAt(start) != Term)) {
123                sResult = sResult + s.charAt(start);
124
125                if (s.charAt(start + 1) == Term)
126                    break;
127                else
128                    start++;
129            }
130
131            // Not a proper string, so nullify it
132            sResult = "";
133        }
134
135        if (s.charAt(start) != Term)
136            start++;
137
138        if (Constants.DEBUG) {
139            System.out.println("getChar - \"" + sResult + "\"");
140        }
141
142        if (sResult.length() == 0)
143            return '\0';
144        return sResult.charAt(0);
145    }
146
147    /**
148     * Return string parameter from input string. The input may either be in Hollerith
149     * form (nH...), as per the IGES specification delimited by the global delimiter
150     * character or whatever characters are found between the delimiter character are
151     * returned. If the String equals "NULL", then an empty String is returned.
152     *
153     * @param s input string from IGES file.
154     * @return string stripped of Hollerith prefix, or empty string if the input string is
155     *         invalid.
156     */
157    private String getString(String s) {
158
159        while (s.charAt(start) == ' ')
160            start++;
161
162        //  Deal with hitting the terminator right away.
163        int c = s.charAt(start);
164        if (c == Delim || c == Term) {
165            if (c == Delim)
166                ++start;        //  Advance only if it is the delimiter that is hit.
167            return "";
168        }
169
170        // Pull out everything between the delimiters.
171        int newstart = start + 1;
172        c = s.charAt(newstart);
173        while (c != Delim && c != Term) {
174            ++newstart;
175            c = s.charAt(newstart);
176        }
177        String sResult = s.substring(start, newstart);
178
179        //  Now convert from Hollerith form (if necessary).
180        try {
181            // Should be in form ##Hchars
182            int sStart = s.indexOf('H', start);
183
184            int iLen = Integer.parseInt((s.substring(start, sStart)).trim());
185            int end = sStart + 2 + iLen;
186
187            if (end < 0)
188                sResult = "";
189            else {
190                sResult = s.substring(sStart + 1, end - 1);
191                if (end - 1 > newstart)
192                    newstart = end - 1;
193            }
194
195        } catch (Exception e) {
196            //  Do nothing. Keep the entire string.
197        }
198
199        start = newstart;
200        if (s.charAt(start) != Term)
201            start++;
202
203        if ("NULL".equals(sResult))
204            sResult = "";
205
206        if (Constants.DEBUG)
207            System.out.println("getString - \"" + sResult + "\"");
208
209        return sResult;
210    }
211
212    /**
213     * Return real parameter from input string. The input string can be of single
214     * (1.0e+010) or double (1.0d+010) precision, and delimited by the global delimiter
215     * character. The field is blank, then Double.NaN is returned.
216     *
217     * @param s input string from IGES file.
218     * @return real number
219     */
220    private double getReal(String s) {
221        String sResult = "";
222
223        while (s.charAt(start) == ' ')
224            start++;
225
226        //  Deal with hitting the terminator right away.
227        int c = s.charAt(start);
228        if (c == Delim || c == Term) {
229            if (c == Delim)
230                ++start;        //  Advance only if it is the delimiter that is hit.
231            return Double.NaN;
232        }
233
234        while ((s.charAt(start) != Delim) && (s.charAt(start) != Term)) {
235            sResult = sResult + s.charAt(start);
236
237            if (s.charAt(start + 1) == Term)
238                break;
239            else
240                start++;
241        }
242
243        if (s.charAt(start) != Term)
244            start++;
245
246        sResult = sResult.trim();
247
248        if (Constants.DEBUG)
249            System.out.println("getReal - \"" + sResult + "\"");
250
251        return Constants.toDouble(sResult);
252    }
253
254    /**
255     * Return integer parameter from input string. The input string can be represent
256     * either a standard integer, or a float value (e.g. 1.000e+010), and delimited by the
257     * global delimiter character. If the field is blank, a 0 is returned.
258     *
259     * @param s input string from IGES file.
260     * @return integer number
261     */
262    private int getInt(String s) {
263        String sResult = "";
264
265        while (s.charAt(start) == ' ')
266            start++;
267
268        //  Deal with hitting the terminator right away.
269        int c = s.charAt(start);
270        if (c == Delim || c == Term) {
271            if (c == Delim)
272                ++start;        //  Advance only if it is the delimiter that is hit.
273            return 0;
274        }
275
276        while ((s.charAt(start) != Delim) && (s.charAt(start) != Term)) {
277            sResult = sResult + s.charAt(start);
278
279            if (s.charAt(start + 1) == Term)
280                break;
281            else
282                start++;
283        }
284
285        if (s.charAt(start) != Term)
286            start++;
287
288        sResult = sResult.trim();
289
290        if (Constants.DEBUG) {
291            System.out.println("getInt - \"" + sResult + "\"");
292        }
293
294        return Constants.toInt(sResult);
295    }
296
297    /**
298     * Return parameter delimiter character. This indicates which character is used to
299     * separate parameter values in the Global and Parameter Data sections. The default
300     * value is “comma.”
301     *
302     * @return parameter delimiter character
303     */
304    public char getDelim() {
305        return Delim;
306    }
307
308    /**
309     * Return parameter terminator character. This indicates which character denotes the
310     * end of parameters in the Global Section and in each Parameter Data Section entry.
311     * The default value is “semicolon.”
312     *
313     * @return parameter terminator character
314     */
315    public char getTerm() {
316        return Term;
317    }
318
319    /**
320     * Return the IGES product identification from the sending system for this Part. This
321     * contains the name or identifier which is used by the sender reference this product.
322     * 
323     * @return The IGES product identification of the sending system.
324     */
325    public String getProductName() {
326        return PName;
327    }
328
329    /**
330     * Set the IGES product identification from the sending system for this Part. This
331     * contains the name or identifier which is used by the sender reference this product.
332     *
333     * @param productName The name the sender uses to reference this product (Part).
334     */
335    public void setProductName(String productName) {
336        PName = productName;
337    }
338
339    /**
340     * Return the IGES file name record which contains the name of the exchange file.
341     * 
342     * @return The IGES file name
343     */
344    public String getFileName() {
345        return FName;
346    }
347
348    /**
349     * Set the file name of the exchange file.
350     * 
351     * @param fileName The file name for the exchange file.
352     */
353    public void setFileName(String fileName) {
354        FName = fileName;
355    }
356
357    /**
358     * Return the Native System ID which identifies the native system software which
359     * created the native format file used to generate this exchange file.
360     * 
361     * @return The Native System ID
362     */
363    public String getSystemID() {
364        return SysID;
365    }
366
367    /**
368     * Set the Native System ID which identifies the native system software which created
369     * the native format file used to generate this exchange file.
370     * 
371     * @param sysID The Native System ID to set.
372     */
373    public void setSystemID(String sysID) {
374        SysID = sysID;
375    }
376
377    /**
378     * Return the IGES file preprocessor version. This uniquely identifies the version or
379     * release date of the preprocessor which created this file.
380     * @return The IGES file preprocessor version
381     */
382    public String getPreprocesorVersion() {
383        return TranVrsn;
384    }
385
386    /**
387     * Set the preprocessor version. This uniquely identifies the version or release date
388     * of the preprocessor which created this file.
389     * 
390     * @param version The IGES file preprocessor version to set.
391     */
392    public void setPreprocessorVersion(String version) {
393        TranVrsn = version;
394    }
395
396    /**
397     * Return the model length unit's used in the IGES file.
398     *
399     * @return The length units used in this transfer.
400     */
401    public Unit<Length> getUnit() {
402        Unit unit = NonSI.INCH;
403        switch (UnitFlg) {
404            case 2:
405                unit = SI.MILLIMETER;
406                break;
407            case 3:
408                //  The unit is identified by symbol, not by code.
409                switch (UnitType) {
410                    case "MM":
411                        unit = SI.MILLIMETER;
412                        break;
413                    case "FT":
414                        unit = NonSI.FOOT;
415                        break;
416                    case "MI":
417                        unit = NonSI.MILE;
418                        break;
419                    case "M":
420                        unit = SI.METER;
421                        break;
422                    case "KM":
423                        unit = SI.KILOMETER;
424                        break;
425                    case "MIL":
426                        unit = NonSI.INCH.times(0.001);
427                        break;
428                    case "UM":
429                        unit = SI.METER.times(1.e-6);
430                        break;
431                    case "CM":
432                        unit = SI.CENTIMETER;
433                        break;
434                    case "UIN":
435                        unit = NonSI.INCH.times(1.e-6);
436                        break;
437                }
438                break;
439            case 4:
440                unit = NonSI.FOOT;
441                break;
442            case 5:
443                unit = NonSI.MILE;
444                break;
445            case 6:
446                unit = SI.METER;
447                break;
448            case 7:
449                unit = SI.KILOMETER;
450                break;
451            case 8:
452                unit = NonSI.INCH.times(0.001);
453                break;
454            case 9:
455                unit = SI.METER.times(1.e-6);
456                break;
457            case 10:
458                unit = SI.CENTIMETER;
459                break;
460            case 11:
461                unit = NonSI.INCH.times(1.e-6);
462                break;
463        }
464        return unit;
465    }
466
467    /**
468     * Set the model length unit's used in the IGES file.
469     * 
470     * @param unit The model length unit to save the data in.
471     */
472    public void setUnit(Unit<Length> unit) {
473        //  Turn the input unit into a unit flag and type value.
474        if (SI.MILLIMETER.equals(unit)) {
475            UnitFlg = 2;
476            UnitType = "MM";
477
478        } else if (NonSI.FOOT.equals(unit)) {
479            UnitFlg = 4;
480            UnitType = "FT";
481
482        } else if (NonSI.MILE.equals(unit)) {
483            UnitFlg = 5;
484            UnitType = "MI";
485
486        } else if (SI.METER.equals(unit)) {
487            UnitFlg = 6;
488            UnitType = "M";
489
490        } else if (SI.KILOMETER.equals(unit)) {
491            UnitFlg = 7;
492            UnitType = "KM";
493
494        } else if (NonSI.INCH.times(0.001).equals(unit)) {
495            UnitFlg = 8;
496            UnitType = "MIL";
497
498        } else if (SI.METER.times(1.e-6).equals(unit)) {
499            UnitFlg = 9;
500            UnitType = "UM";
501
502        } else if (SI.CENTIMETER.equals(unit)) {
503            UnitFlg = 10;
504            UnitType = "CM";
505
506        } else if (NonSI.INCH.times(1.e-6).equals(unit)) {
507            UnitFlg = 11;
508            UnitType = "UIN";
509
510        } else {
511            //  Default to inches.
512            UnitFlg = 1;
513            UnitType = "IN";
514        }
515    }
516
517    /**
518     * Return number of line weights.
519     *
520     * @return number of line weights
521     */
522    public int getLineWeights() {
523        return LineWeights;
524    }
525
526    /**
527     * Return maximum line width.
528     *
529     * @return maximum line width
530     */
531    public double getLineWidth() {
532        return LineWidth;
533    }
534
535    /**
536     * Return the date and time of IGES file generation.
537     * 
538     * @return The date and time of IGES file generation
539     */
540    public String getDateTime() {
541        return DateTime;
542    }
543
544    /**
545     * Return Minimum User-Intended Resolution Granularity value. This specifies the
546     * smallest distance between coordinates, in model-space units, that the receiving
547     * system shall consider as discernible.
548     *
549     * @return the granularity value
550     */
551    public double getGrain() {
552        return Grain;
553    }
554
555    /**
556     * Return Minimum User-Intended Resolution Granularity value. This specifies the
557     * smallest distance between coordinates, in model-space units, that the receiving
558     * system shall consider as discernible.
559     *
560     * @return the granularity value as a Parameter object.
561     */
562    public Parameter<Length> getGrainParameter() {
563        return Parameter.valueOf(Grain, getUnit());
564    }
565
566    /**
567     * Set the Minimum User-Intended Resolution or Granularity value. This specifies the
568     * smallest distance between coordinates, in model-space units, that the receiving
569     * system shall consider as discernible.
570     *
571     * @param value The granularity value
572     */
573    public void setGrain(double value) {
574        Grain = value;
575    }
576
577    /**
578     * Return the date and time of the last modification of the model in this exchange
579     * file.
580     * @return The date and time of the last modification
581     */
582    public String getModDateTime() {
583        return ModDate;
584    }
585
586    /**
587    *  Set the date and time of the last modification of the model in this exchange file.
588    * 
589     * @param modDate The date and time of the last modification as a String.
590    */
591    public void setModDateTime(String modDate) {
592        ModDate = modDate;
593    }
594    
595    /**
596    * Return the name of the person who created this exchange file.
597    *
598    * @return the author identification
599    */
600    public String getAuthor() {
601        return Author;
602    }
603    
604    /**
605    *  Set the name of the person who created this exchange file.
606    * 
607     * @param author The name of the author of the file.
608    */
609    public void setAuthor(String author) {
610        Author = author;
611    }
612    
613    /**
614    * Return the name of the organization or group with whom the author is associated.
615    *
616    * @return the author's organization identification
617    */
618    public String getOrganization() {
619        return Organization;
620    }
621    
622    /**
623    *  Set the name of the organization or group with whom the author is associated.
624    * 
625     * @param organization The name of the organization the author is associated with.
626    */
627    public void setOrganization(String organization) {
628        Organization = organization;
629    }
630    
631    /**
632    *  Return the version of the IGES Specification to which the data in this file complies.
633    * 
634     * @return The version of the IGES Specification to which the file complies.
635    */
636    public int getSpecVersion() {
637        return SpecVrsn;
638    }
639    
640    
641    /**
642     * Read the Global Section from the input file. This method can handle missing or
643     * incorrect fields.
644     *
645     * @param in input file
646     * @throws IOException if there is any problem reading the IGES file.
647     */
648    public void read(RandomAccessFile in) throws IOException {
649        boolean ok = false;
650
651        StringBuilder buffer = new StringBuilder();
652        while (true) {
653            long curloc = in.getFilePointer();
654            String line = Constants.myReadLine(in);
655            if (line.charAt(Constants.SECTID_COL) == 'G') {
656                //  Deal with non-standard blank spaces between PDID_COL and SECTID_COL.
657                int end = Constants.SECTID_COL;
658                String rhs = line.substring(Constants.PDID_COL,Constants.SECTID_COL).trim();
659                if (rhs.length() < 1)
660                    end = Constants.PDID_COL;
661                buffer.append( line.substring(0,end) );
662            } else {
663                in.seek(curloc);
664                ok = true;
665                break;
666            }
667        }
668        String s = buffer.toString();
669        
670        if (ok) {
671            Delim        = getChar(s); if (Delim == '\0') Delim = ',';
672            Term         = getChar(s); if (Term  == '\0') Term  = ';';
673            PName        = getString(s);
674            FName        = getString(s);
675            SysID        = getString(s);
676            TranVrsn     = getString(s);
677            IntSize      = getInt(s);
678            FloatExp     = getInt(s);
679            FloatMant    = getInt(s);
680            DoubleExp    = getInt(s);
681            DoubleMant   = getInt(s);
682            DestName     = getString(s);
683            ModelScale   = getReal(s);
684            UnitFlg      = getInt(s);
685            UnitType     = getString(s);
686            LineWeights  = getInt(s);
687            LineWidth    = getReal(s);
688            DateTime     = getString(s);
689            Grain        = getReal(s);
690            MaxValue     = getReal(s);
691            if (Double.isNaN(MaxValue))
692                MaxValue = 0;
693            Author       = getString(s);
694            Organization = getString(s);
695            SpecVrsn     = getInt(s);
696            DraftStd     = getInt(s);
697            ModDate      = getString(s);
698            AppProtocol  = getString(s);
699        }
700
701        StringBuilder outStr = new StringBuilder();
702        outStr.append("Delim        = \""); outStr.append(Delim);       outStr.append("\"\n");
703        outStr.append("Term         = \""); outStr.append(Term);        outStr.append("\"\n");
704        outStr.append("PName        = \""); outStr.append(PName);       outStr.append("\"\n");
705        outStr.append("FName        = \""); outStr.append(FName);       outStr.append("\"\n");
706        outStr.append("SysID        = \""); outStr.append(SysID);       outStr.append("\"\n");
707        outStr.append("TranVrsn     = \""); outStr.append(TranVrsn);    outStr.append("\"\n");
708        outStr.append("IntSize      = ");   outStr.append(IntSize);     outStr.append("\n");
709        outStr.append("FloatExp     = ");   outStr.append(FloatExp);    outStr.append("\n");
710        outStr.append("FloatMant    = ");   outStr.append(FloatMant);   outStr.append("\n");
711        outStr.append("DoubleExp    = ");   outStr.append(DoubleExp);   outStr.append("\n");
712        outStr.append("DoubleMant   = ");   outStr.append(DoubleMant);  outStr.append("\n");
713        outStr.append("DestName     = \""); outStr.append(DestName);    outStr.append("\"\n");
714        outStr.append("ModelScale   = ");   outStr.append(ModelScale);  outStr.append("\n");
715        outStr.append("UnitFlg      = ");   outStr.append(UnitFlg);     outStr.append("\n");
716        outStr.append("UnitType     = \""); outStr.append(UnitType);    outStr.append("\"\n");
717        outStr.append("LineWeights  = ");   outStr.append(LineWeights); outStr.append("\n");
718        outStr.append("LineWidth    = ");   outStr.append(LineWidth);   outStr.append("\n");
719        outStr.append("DateTime     = \""); outStr.append(DateTime);    outStr.append("\"\n");
720        outStr.append("Grain        = ");   outStr.append(Grain);       outStr.append("\n");
721        outStr.append("MaxValue     = ");   outStr.append(MaxValue);    outStr.append("\n");
722        outStr.append("Author       = \""); outStr.append(Author);      outStr.append("\"\n");
723        outStr.append("Organization = \""); outStr.append(Organization);outStr.append("\"\n");
724        outStr.append("SpecVrsn     = ");   outStr.append(SpecVrsn);    outStr.append("\n");
725        outStr.append("DraftStd     = ");   outStr.append(DraftStd);    outStr.append("\n");
726        outStr.append("ModDate      = \""); outStr.append(ModDate);     outStr.append("\n");
727        outStr.append("AppProtocol  = \""); outStr.append(AppProtocol); outStr.append("\"\n");
728
729        GlobalText = outStr.toString();
730    }
731
732    /**
733     * Write the Global Section to the specified writer.
734     *
735     * @param writer The PrintWriter to write the Global Section to.
736     * @return The number of lines written out to the global section.
737     * @throws IOException if there is any problem writing the section.
738     */
739    public int write(PrintWriter writer) throws IOException {
740       
741        //  Construct the current date/time for output.
742        SimpleDateFormat dformat = new SimpleDateFormat("yyyyMMdd.HHmmss");
743        DateTime = dformat.format(new Date());
744        
745        //  Construct a long string with all the section's parameters in it.
746        StringBuilder buffer = new StringBuilder();
747        buffer.append(makeHollerith(Delim));        buffer.append(Delim);
748        buffer.append(makeHollerith(Term));         buffer.append(Delim);
749        buffer.append(makeHollerith(PName));        buffer.append(Delim);
750        buffer.append(makeHollerith(FName));        buffer.append(Delim);
751        buffer.append(makeHollerith(SysID));        buffer.append(Delim);
752        buffer.append(makeHollerith(TranVrsn));     buffer.append(Delim);
753        buffer.append(IntSize);                     buffer.append(Delim);
754        buffer.append(FloatExp);                    buffer.append(Delim);
755        buffer.append(FloatMant);                   buffer.append(Delim);
756        buffer.append(DoubleExp);                   buffer.append(Delim);
757        buffer.append(DoubleMant);                  buffer.append(Delim);
758        buffer.append(makeHollerith(DestName));     buffer.append(Delim);
759        buffer.append(ModelScale);                  buffer.append(Delim);
760        buffer.append(UnitFlg);                     buffer.append(Delim);
761        buffer.append(makeHollerith(UnitType));     buffer.append(Delim);
762        buffer.append(LineWeights);                 buffer.append(Delim);
763        buffer.append(LineWidth);                   buffer.append(Delim);
764        buffer.append(makeHollerith(DateTime));     buffer.append(Delim);
765        buffer.append(Grain);                       buffer.append(Delim);
766        buffer.append(MaxValue);                    buffer.append(Delim);
767        buffer.append(makeHollerith(Author));       buffer.append(Delim);
768        buffer.append(makeHollerith(Organization)); buffer.append(Delim);
769        buffer.append(SpecVrsn);                    buffer.append(Delim);
770        buffer.append(DraftStd);                    buffer.append(Delim);
771        buffer.append(makeHollerith(ModDate));      buffer.append(Delim);
772        buffer.append(makeHollerith(AppProtocol));  buffer.append(Term);
773
774        //  Now write out the string of data to form the global section.
775        int index = Constants.writeSection(writer, 1, "", 'G', buffer);
776
777        return index - 1;
778    }
779
780    /**
781     * Take the input and turn it into a Hollerith String.
782     */
783    private static String makeHollerith(String input) {
784        if (input.length() == 0)
785            return input;
786        StringBuilder buffer = new StringBuilder();
787        buffer.append(input.length());
788        buffer.append("H");
789        buffer.append(input);
790        return buffer.toString();
791    }
792
793    /**
794     * Take the input and turn it into a Hollerith String.
795     */
796    private static String makeHollerith(char input) {
797        StringBuilder buffer = new StringBuilder();
798        buffer.append(1);
799        buffer.append("H");
800        buffer.append(input);
801        return buffer.toString();
802    }
803
804    /**
805     * Dump to String.
806     *
807     * @return String containing the resulting text.
808     */
809    @Override
810    public String toString() {
811        return GlobalText;
812    }
813}