001/*
002 *   Entity  -- Superclass for all IGES entity types.
003 *
004 *   Copyright (C) 2010-2016, Joseph A. Huwaldt. All rights reserved.
005 *   
006 *   This library is free software; you can redistribute it and/or
007 *   modify it under the terms of the GNU Lesser General Public
008 *   License as published by the Free Software Foundation; either
009 *   version 2.1 of the License, or (at your option) any later version.
010 *   
011 *   This library is distributed in the hope that it will be useful,
012 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *   Lesser General Public License for more details.
015 *
016 *   You should have received a copy of the GNU Lesser General Public License
017 *   along with this program; if not, write to the Free Software
018 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
019 *   Or visit:  http://www.gnu.org/licenses/lgpl.html
020 *
021 *   Based on, but heavily modified from, IGESView ( http://ts.nist.gov/Standards/IGES/igesTools.cfm )
022 */
023package geomss.geom.reader.iges;
024
025import java.io.PrintWriter;
026import java.io.RandomAccessFile;
027import java.io.IOException;
028import java.text.MessageFormat;
029import java.util.List;
030import java.util.ArrayList;
031import java.util.ResourceBundle;
032import geomss.geom.GeomPoint;
033import geomss.geom.Point;
034import geomss.geom.GTransform;
035
036/**
037 * The Entity class is meant to be a superclass for the individual entity type/ form
038 * classes. This class also contains some utility functions to make reading of the entity
039 * easier. When the entity is read in, it will be assumed that the first read call will
040 * return the first Parameter Data line for that entity.
041 *
042 * <p> Modified by: Joseph A. Huwaldt </p>
043 *
044 * @author JDN, AED, Version 1.0
045 * @version September 13, 2016
046 */
047public abstract class Entity {
048
049    /**
050     * The String resources used by this package.
051     */
052    protected static final ResourceBundle RESOURCES = Constants.RESOURCES;
053
054    /**
055     * List of DE's of associativities
056     */
057    protected List<Integer> associativities = new ArrayList();
058
059    /**
060     * List of DE's of properties
061     */
062    protected List<Integer> properties = new ArrayList();
063
064    /**
065     * Directory Entry for this entity
066     */
067    private final DirEntry DE;
068
069    /**
070     * Index of start of current parameter in input PD string
071     */
072    private int start = 0;
073
074    /**
075     * Parameter Data string to be read in and parsed
076     */
077    private String PDstring = "";
078
079    /**
080     * Error report for this entity
081     */
082    private final List<String> errors = new ArrayList();
083
084    /**
085     * The active part to which this entity belongs
086     */
087    private final Part part;
088
089    // Constants that only Entity and its subclasses need to know
090    protected static final int ENTSTAT_VISIBLE = 0;
091    protected static final int ENTSTAT_BLANKED = 1;
092
093    protected static final int ENTSTAT_INDEPENDENT = 0;
094    protected static final int ENTSTAT_PHYSDEPENDENT = 1;
095    protected static final int ENTSTAT_LOGDEPENDENT = 2;
096    protected static final int ENTSTAT_PHYSLOGDEPENDENT = 3;
097
098    protected static final int ENTSTAT_GEOMETRY = 0;
099    protected static final int ENTSTAT_ANNOTATION = 1;
100    protected static final int ENTSTAT_DEFINITION = 2;
101    protected static final int ENTSTAT_OTHER = 3;
102    protected static final int ENTSTAT_LOGPOSITION = 4;
103    protected static final int ENTSTAT_2DPARAMETRIC = 5;
104    protected static final int ENTSTAT_CONSTRUCTION = 6;
105
106    protected static final int ENTSTAT_UNDEFINED = -1;
107
108    /**
109     * Default constructor for the Entity superclass. Sets a pointer back to the main Part
110     * and sets anything else to their defaults.
111     *
112     * @param p  part in which this entity is contained
113     * @param de Directory Entry for this entity
114     * @see DirEntry#read
115     */
116    public Entity(Part p, DirEntry de) {
117        part = p;
118        DE = new DirEntry(de);
119    }
120
121    /**
122     * Reads the entity from the input file "in". It is assumed that the first
123     * myReadLine() call will return the first Parameter Data line for this entity. The PD
124     * data will be read into StringBuffer "PDstring", which will then be parsed by the
125     * Entity???.read() method.
126     *
127     * @param in input file, which is of class RandomAccessFile
128     * @throws IOException if there is any problem reading the entity from the IGES file.
129     */
130    public void read(RandomAccessFile in) throws IOException {
131        StringBuilder buffer = new StringBuilder();
132
133        if (Constants.DEBUG) {
134            System.out.println("Entity.read() called\n");
135        }
136
137        for (int i = 0; i < DE.getPDCnt(); i++) {
138            long curloc = in.getFilePointer();
139            String line = Constants.myReadLine(in);
140            if (line.charAt(Constants.SECTID_COL) == 'P')
141                buffer.append(line.substring(0, Constants.PDID_COL));
142            else {
143                in.seek(curloc);
144                break;
145            }
146        }
147
148        PDstring = buffer.toString();
149        int PDType = getInt(PDstring); // Read in the type from the PD Data string, check against what is in DE.
150        if (PDType != getType()) {
151            StringBuilder msg = new StringBuilder(RESOURCES.getString("warning"));
152            msg.append(getTypeString());
153            msg.append(", DE ");
154            msg.append(getDENum());
155            msg.append(":  ");
156            msg.append(MessageFormat.format(RESOURCES.getString("entityTypeMissmatch"), PDType, getType()));
157            addErrorMessage(msg.toString());
158        }
159    }
160
161    /**
162     * Reads in the "additional pointers" after the regular Parameter Data. These are
163     * pointers to associativities and properties.
164     */
165    public void read_additional() {
166        String s = PDstring;
167
168        int num_assoc = getInt(s);
169        for (int i = 0; i < num_assoc; i++)
170            associativities.add(getInt(s));
171
172        int num_props = getInt(s);
173        for (int i = 0; i < num_props; i++)
174            properties.add(getInt(s));
175    }
176
177    /**
178     * Returns <code>true</code> if the Entity can be written to an exchange file. The
179     * default implementation returns <code>false</code> indicating that this Entity can
180     * not be written to a file.
181     *
182     * @return true if the Entity can be written to an exchange file.
183     */
184    public boolean canWrite() {
185        return false;
186    }
187
188    /**
189     * Write this entities parameter data to the specified PrintWriter. The default
190     * implementation always throws an exception.
191     *
192     * @param writer The PrintWriter to write the parameter data for this entity to.
193     * @param PDNum  The starting Parameter Data row index number.
194     * @return The Parameter Data row index number for the next row.
195     * @throws java.io.IOException This implementation always throws this exception.
196     */
197    public int write(PrintWriter writer, int PDNum) throws IOException {
198        throw new IOException(
199                MessageFormat.format(RESOURCES.getString("canNotWrite"), getTypeString()));
200    }
201
202    /**
203     * Checks to see if the entity has any errors or warnings.
204     */
205    public abstract void check();
206
207    /**
208     * Return the Parameter Data string for this entity.
209     *
210     * @return The Parameter Data string for this entity.
211     */
212    protected String getPDString() {
213        return PDstring;
214    }
215
216    /**
217     * Return a reference to the directory entry for this entity.
218     *
219     * @return A reference to the directory entry for this entity.
220     */
221    protected DirEntry getDirectoryEntry() {
222        return DE;
223    }
224
225    /**
226     * Return a reference to the Part for this entity.
227     *
228     * @return A reference to the Part for this entity.
229     */
230    protected Part getPart() {
231        return part;
232    }
233
234    /**
235     * Add a new entry to the list of error/warning messages for this entity.
236     *
237     * @param msg The message to be added to the list of error/warning messages.
238     */
239    protected void addErrorMessage(String msg) {
240        errors.add(msg);
241    }
242
243    /**
244     * Get a list of error and warning messages issued by this Entity. If there are no
245     * messages, an empty list is returned.
246     *
247     * @return List of error strings
248     */
249    public List<String> getErrors() {
250        return errors;
251    }
252
253    /**
254     * Returns a short String describing this Entity object's type.
255     *
256     * @return A short String describing this Entity object's type.
257     */
258    public abstract String getTypeString();
259
260    /**
261     * Returns a warning message String that is specific to this Entity type, DE # and the
262     * specified message. Using this method allows consistency in warning messages across
263     * Entity types.
264     *
265     * @param msg The specific warning message to be appended to the information about
266     *            this entity.
267     * @return A String representing the input message appended onto information about
268     *         this specific Entity.
269     */
270    protected String getWarningString(String msg) {
271        StringBuilder buffer = new StringBuilder();
272        buffer.append(RESOURCES.getString("warning"));
273        buffer.append(getTypeString());
274        buffer.append(", DE ");
275        buffer.append(DE.getDENum());
276        buffer.append(":  ");
277        buffer.append(msg);
278        return buffer.toString();
279    }
280
281    /**
282     * Dumps a simple header. Just the DE Number and type.
283     *
284     * @return String containing the resulting text.
285     */
286    public String getHeader() {
287        StringBuilder outStr = new StringBuilder("DE");
288        outStr.append(getDirectoryEntry().getDENum());
289        outStr.append(" - ");
290        outStr.append(getTypeString());
291        return outStr.toString();
292    }
293
294    /**
295     * Dump to String.
296     *
297     * @return String containing the resulting text.
298     */
299    @Override
300    public String toString() {
301        StringBuilder outStr = new StringBuilder(getTypeString());
302        outStr.append("\n");
303
304        outStr.append(DE.toString());
305        outStr.append("\n");
306
307        int num_assoc = associativities.size();
308        if (num_assoc > 0) {
309            outStr.append("Associativities:\n");
310            for (int i = 0; i < num_assoc; i++) {
311                outStr.append("  assoc(");
312                outStr.append(i);
313                outStr.append(") = ");
314                outStr.append(associativities.get(i).intValue());
315                outStr.append("\n");
316            }
317            outStr.append("\n");
318        }
319
320        int num_props = properties.size();
321        if (num_props > 0) {
322            outStr.append("Properties:\n");
323            for (int i = 0; i < num_props; i++) {
324                outStr.append("  prop(");
325                outStr.append(i);
326                outStr.append(") = ");
327                outStr.append(properties.get(i).intValue());
328                outStr.append("\n");
329            }
330            outStr.append("\n");
331        }
332
333        return outStr.toString();
334    }
335
336    /**
337     * Return blank status. Returns 0 if the entity is visible, 1 if it is blanked.
338     *
339     * @return blank status
340     */
341    public int blankedStatus() {
342        String sub = DE.getStatus().substring(0, 2);
343
344        return Constants.toInt(sub);
345    }
346
347    /**
348     * Return subordinate status. Returns 0 if the entity is independent, 1 if it is
349     * physically dependent to its parent, 2 if it is logically dependent to its parent,
350     * and 3 if it is both physically and logically dependent to its parent.
351     *
352     * @return subordinate status
353     */
354    public int subordStatus() {
355        String sub = DE.getStatus().substring(2, 4);
356
357        return Constants.toInt(sub);
358    }
359
360    /**
361     * Return usage status. Returns 0 if the entity is geometry, 1 if it is annotation, 2
362     * if it is definition, 3 if it is something else, 4 if it is logical/positional, 5 if
363     * it is 2D parametric, and 6 if it is construction geometry.
364     *
365     * @return entity usage status
366     */
367    public int useStatus() {
368        String sub = DE.getStatus().substring(4, 6);
369
370        return Constants.toInt(sub);
371    }
372
373    /**
374     * Return hierarchy status. Returns 0 if the entity has global top down hierarchy, 1
375     * if global defer, or 2 if a hierarchy property is to be used.
376     *
377     * @return hierarchy status
378     */
379    public int hierStatus() {
380        String sub = DE.getStatus().substring(6, 8);
381
382        return Constants.toInt(sub);
383    }
384
385    /**
386     * Get matrix representing entity's matrix appended to supplied matrix.
387     *
388     * @param m matrix to which to append this entities matrix.
389     * @return combined matrix: m * m(entity)
390     */
391    public GTransform getMatrix(GTransform m) {
392        int id = DE.getMatrix();
393        if (id == 0)
394            return m;
395
396        // This handles nested matrices
397        GTransform me = ((Entity124_TransformationMatrix)part.getEntity(id)).getMat();
398        GTransform m1 = m.times(me);
399
400        return m1;
401    }
402
403    /**
404     * Return Directory Entry number.
405     *
406     * @return DE number for this entity
407     */
408    public int getDENum() {
409        return DE.getDENum();
410    }
411
412    /**
413     * Return Entity type.
414     *
415     * @return type
416     */
417    public int getType() {
418        return DE.getType();
419    }
420
421    /**
422     * Return Parameter Data number.
423     *
424     * @return PD number for this entity
425     */
426    public int getPDNum() {
427        return DE.getPDNum();
428    }
429
430    /**
431     * Return View.
432     *
433     * @return View for this entity
434     */
435    public int getView() {
436        return DE.getView();
437    }
438
439    /**
440     * Return char parameter from input string. The character string must be in Hollerith
441     * form (1H-), as per the IGES specification, and delimited by the global delimiter
442     * character.
443     *
444     * @param s input string
445     * @return single character
446     */
447    protected char getChar(String s) {
448        String sResult = "";
449
450        while (s.charAt(start) == ' ') {
451            start++;
452        }
453
454        if ((s.charAt(start) != Constants.Delim) && (s.charAt(start) != Constants.Term)) {
455            // Should be in form ##Hchars
456            int sStart = s.indexOf('H', start);
457            int iLen = Integer.parseInt((s.substring(start, sStart)).trim());
458            int newstart = sStart + 2 + iLen;
459
460            if (newstart < 0) {
461                sResult = "";
462            } else {
463                sResult = s.substring(sStart + 1, newstart - 1);
464                start = newstart - 1;
465            }
466        }
467
468        if (s.charAt(start) != Constants.Term)
469            start++;
470
471        if (Constants.DEBUG) {
472            System.out.println("getChar - \"" + sResult + "\"");
473        }
474
475        if (sResult.length() == 0)
476            return '\0';
477        return sResult.charAt(0);
478    }
479
480    /**
481     * Return string parameter from input string. The string parameter must be in
482     * Hollerith form (nH...), as per the IGES specification, and delimited by the global
483     * delimiter character.
484     *
485     * @param s input string
486     * @return string stripped of Hollerith prefix, or empty string if the input string is
487     *         invalid
488     */
489    protected String getString(String s) {
490        String sResult = "";
491
492        while (s.charAt(start) == ' ') {
493            start++;
494        }
495
496        if ((s.charAt(start) != Constants.Delim) && (s.charAt(start) != Constants.Term)) {
497            // Should be in form ##Hchars
498            int sStart = s.indexOf('H', start);
499            int iLen = Integer.parseInt((s.substring(start, sStart)).trim());
500            int newstart = sStart + 2 + iLen;
501
502            if (newstart < 0) {
503                sResult = "";
504            } else {
505                sResult = s.substring(sStart + 1, newstart - 1);
506                start = newstart - 1;
507            }
508        }
509
510        if (s.charAt(start) != Constants.Term)
511            start++;
512
513        if (Constants.DEBUG) {
514            System.out.println("getString - \"" + sResult + "\"");
515        }
516
517        return sResult;
518    }
519
520    /**
521     * Return real parameter from input string. The real parameter can be of single
522     * (1.0e+010) or double (1.0d+010) precision, and delimited by the global delimiter
523     * character.
524     *
525     * @param s input string
526     * @return real number
527     */
528    protected double getReal(String s) {
529        StringBuffer sResult = new StringBuffer();
530
531        while (s.charAt(start) == ' ') {
532            start++;
533        }
534
535        while ((s.charAt(start) != Constants.Delim) && (s.charAt(start) != Constants.Term)) {
536            sResult.append(s.charAt(start));
537
538            if (s.charAt(start + 1) == Constants.Term)
539                break;
540            else
541                start++;
542        }
543
544        if (s.charAt(start) != Constants.Term)
545            start++;
546
547        if (Constants.DEBUG) {
548            System.out.println("getReal - \"" + sResult + "\"");
549        }
550
551        return Constants.toDouble(sResult.toString());
552    }
553
554    /**
555     * Return real parameter from input string, with default value. The real parameter can
556     * be of single (1.0e+010) or double (1.0d+010) precision, and delimited by the global
557     * delimiter character. If the parameter is invalid or blank, the input default value
558     * is used.
559     *
560     * @param s   input string
561     * @param def The default value to return if the string is invalid or blank.
562     * @return real number
563     */
564    protected double getReal(String s, double def) {
565        StringBuffer sResult = new StringBuffer();
566
567        while (s.charAt(start) == ' ') {
568            start++;
569        }
570
571        while ((s.charAt(start) != Constants.Delim) && (s.charAt(start) != Constants.Term)) {
572            sResult.append(s.charAt(start));
573
574            if (s.charAt(start + 1) == Constants.Term)
575                break;
576            else
577                start++;
578        }
579
580        if (s.charAt(start) != Constants.Term)
581            start++;
582
583        if (Constants.DEBUG) {
584            System.out.println("getReal - \"" + sResult + "\"");
585        }
586
587        String str = sResult.toString().trim();
588        if (str.length() == 0)
589            return def;
590        return Constants.toDouble(str);
591    }
592
593    /**
594     * Return integer parameter from input string. The integer parameter can be
595     * represented either by the standard integer, or as a float value (e.g. 1.000e+010),
596     * and delimited by the global delimiter character.
597     *
598     * @param s input string
599     * @return integer number
600     */
601    protected int getInt(String s) {
602        StringBuffer sResult = new StringBuffer();
603
604        while (s.charAt(start) == ' ') {
605            start++;
606        }
607
608        while ((s.charAt(start) != Constants.Delim) && (s.charAt(start) != Constants.Term)) {
609            sResult.append(s.charAt(start));
610
611            if (s.charAt(start + 1) == Constants.Term)
612                break;
613            else
614                start++;
615        }
616
617        if (s.charAt(start) != Constants.Term)
618            start++;
619
620        if (Constants.DEBUG) {
621            System.out.println("getInt - \"" + sResult + "\"");
622        }
623
624        return Constants.toInt(sResult.toString());
625    }
626
627    /**
628     * Return integer parameter from input string, with default value. The integer
629     * parameter can be represented either by the standard integer, or as a float value
630     * (e.g. 1.000e+010), and delimited by the global delimiter character. If the
631     * parameter is invalid or blank, the input default value is used.
632     *
633     * @param s   input string
634     * @param def The default value for the integer.
635     * @return integer number
636     */
637    protected int getInt(String s, int def) {
638        StringBuffer sResult = new StringBuffer();
639
640        while (s.charAt(start) == ' ') {
641            start++;
642        }
643
644        while ((s.charAt(start) != Constants.Delim) && (s.charAt(start) != Constants.Term)) {
645            sResult.append(s.charAt(start));
646
647            if (s.charAt(start + 1) == Constants.Term)
648                break;
649            else
650                start++;
651        }
652
653        if (s.charAt(start) != Constants.Term)
654            start++;
655
656        if (Constants.DEBUG) {
657            System.out.println("getInt - \"" + sResult + "\"");
658        }
659
660        String str = sResult.toString().trim();
661        if (str.length() == 0)
662            return def;
663        return Constants.toInt(str);
664    }
665
666    /**
667     * Return 3D Point from string.
668     *
669     * @param s input string
670     * @return 3D Point object
671     */
672    protected Point getPoint3(String s) {
673
674        double x = getReal(s);
675        if (Math.abs(x) < Constants.Grain)
676            x = 0;
677        double y = getReal(s);
678        if (Math.abs(y) < Constants.Grain)
679            y = 0;
680        double z = getReal(s);
681        if (Math.abs(z) < Constants.Grain)
682            z = 0;
683
684        return Point.valueOf(x, y, z, Constants.unit);
685    }
686
687    /**
688     * Return 2D Point from string.
689     *
690     * @param s input string
691     * @return 3D Point object with the Z coordinate set to 0.
692     */
693    protected Point getPoint2(String s) {
694
695        double x = getReal(s);
696        if (Math.abs(x) < Constants.Grain)
697            x = 0;
698        double y = getReal(s);
699        if (Math.abs(y) < Constants.Grain)
700            y = 0;
701
702        return Point.valueOf(x, y, 0, Constants.unit);
703    }
704
705    /**
706     * Append a 2D Point to the specified StringBuffer with each coordinate separated by
707     * the delimiter character.
708     *
709     * @param buffer The buffer to write the point to.
710     * @param point  The 2D point to write out.
711     * @return A reference to the input buffer.
712     */
713    protected StringBuilder appendPoint2(StringBuilder buffer, GeomPoint point) {
714        double x = point.getValue(0);
715        if (Math.abs(x) < Constants.Grain)
716            x = 0;
717        double y = point.getValue(1);
718        if (Math.abs(y) < Constants.Grain)
719            y = 0;
720
721        buffer.append(x);   buffer.append(Constants.Delim);
722        buffer.append(y);   buffer.append(Constants.Delim);
723
724        return buffer;
725    }
726
727    /**
728     * Append a 3D Point to the specified StringBuffer with each coordinate separated by
729     * the delimiter character.
730     *
731     * @param buffer The buffer to write the point to.
732     * @param point  The point to write out.
733     * @return A reference to the input buffer.
734     */
735    protected StringBuilder appendPoint3(StringBuilder buffer, GeomPoint point) {
736        double x = point.getValue(0);
737        if (Math.abs(x) < Constants.Grain)
738            x = 0;
739        double y = point.getValue(1);
740        if (Math.abs(y) < Constants.Grain)
741            y = 0;
742        double z = point.getValue(2);
743        if (Math.abs(z) < Constants.Grain)
744            z = 0;
745        buffer.append(x);   buffer.append(Constants.Delim);
746        buffer.append(y);   buffer.append(Constants.Delim);
747        buffer.append(z);   buffer.append(Constants.Delim);
748        return buffer;
749    }
750
751    /**
752     * Return View object from string. The View is used in the type 404 entity (Drawing).
753     *
754     * @param s input string
755     * @param f drawing form
756     * @return View object
757     */
758    protected View getView(String s, int f) {
759        View v = new View();
760
761        v.viewde = getInt(s);
762        v.origin = getPoint2(s);
763        v.angle = (f == 1) ? getReal(s) : 0.0;
764
765        return v;
766    }
767
768}