001/*
002 *   Part  -- This class encapsulates the entire IGES file.
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.StringWriter;
027import java.io.RandomAccessFile;
028import java.io.IOException;
029import java.util.List;
030import java.util.ArrayList;
031
032/**
033 * The Part class encapsulates the entire IGES file.
034 * 
035 * <p> Modified by: Joseph A. Huwaldt </p>
036 * 
037 * @author JDN, CHP 7/31/98; 406-592 to 406-38 per IGES 6.0, Version 1.0
038 * @version September 13, 2016
039 */
040public class Part {
041
042    private final StartSection startSection = new StartSection();
043    private final GlobalSection globalSection = new GlobalSection();
044    private final List<Entity> entities = new ArrayList();
045    private final List<String> errors = new ArrayList();
046    private final List<Entity> drawings = new ArrayList();
047    private final List<Entity> views = new ArrayList();
048
049    /**
050     * Directory of base file. Used for images and URL's.
051     */
052    public String dir = "";
053
054    private static final int SORT_BY_DENUM = 1;
055    private static final int SORT_BY_PDNUM = 2;
056
057    /**
058     * Default constructor. This instantiates all of the sub-sections in the IGES file.
059     */
060    public Part() {
061    }
062
063    /**
064     * Read in Part from input IGES file. Reads in Directory Entries first, then Parameter
065     * Data, and then the list of entities is checked for correctness. To allow Parameter
066     * Data entries to not be in the same order as the Directory Entries, after the DE's
067     * are read in, the entity list is sorted by PD numbers. Then the Parameter Data is
068     * read in, and then the entity list is sorted by DE numbers again.
069     *
070     * @param in input file
071     * @throws IOException if there is any problem reading the IGES file.
072     */
073    public void read(RandomAccessFile in) throws IOException {
074
075        //  Go to the start of the IGES file.
076        in.seek(0);
077
078        //  Read in the header material.
079        startSection.read(in);
080        globalSection.read(in);
081        Constants.initGlobals(globalSection);
082
083        DirEntry de = new DirEntry();
084
085        // Read in the directory entries.
086        //  Allocate appropriate typed storage for all the entities (create the Entity objects).
087        boolean flag;
088        do {
089            flag = de.read(in);
090
091            if (flag) {
092                Entity e = EntityFactory.create(this, drawings, views, de);
093                entities.add(e);
094            }
095
096        } while (flag);
097
098        // Sort by PD numbers so that they are read in correctly no matter how the PD's are ordered
099        sort(0, entities.size() - 1, SORT_BY_PDNUM);
100
101        // Read in all the entities
102        for (Entity entity : entities) {
103            entity.read(in);
104            entity.check();
105        }
106
107        // Sort to DE number order
108        sort(0, entities.size() - 1, SORT_BY_DENUM);
109
110        //  Create the GeomSS geometry elements (if any).
111        for (Entity entity : entities) {
112            if (entity instanceof GeomSSEntity) {
113                try {
114                    ((GeomSSEntity)entity).createGeometry();
115                } catch (Exception e) {
116                    e.printStackTrace();
117                    System.out.println("IGES Read Error:  " + e.getMessage());
118                    entity.addErrorMessage(e.getMessage());
119                }
120            }
121        }
122
123        // Now output any errors
124        for (Entity entity : entities) {
125            List<String> msgs = entity.getErrors();
126            errors.addAll(msgs);
127        }
128
129    }
130
131    /**
132     * Write this Part to an IGES file.
133     *
134     * @param writer The PrintWriter to write the Part to.
135     * @throws IOException if there is any problem writing the section.
136     */
137    public void write(PrintWriter writer) throws IOException {
138
139        //  Initialize the global constants.
140        Constants.initGlobals(globalSection);
141
142        //  Start by writing out the Start Section.
143        int nStart = startSection.write(writer);
144
145        //  Write out the Global Section.
146        int nGlobal = globalSection.write(writer);
147
148        //  Write all the entity parameter data into a temporary buffer.
149        //  This is needed to track the PDNum and PDCnt for each entity's directory entry.
150        StringWriter str = new StringWriter();
151        int PDnum;
152        try (PrintWriter strWriter = new PrintWriter(str)) {
153            PDnum = 1;
154            for (Entity entity : entities) {
155                if (entity.canWrite()) {
156                    PDnum = entity.write(strWriter, PDnum);
157                }
158            }
159        }
160        int nPD = PDnum - 1;
161
162        //  Write out the directory entries for each entity to be written out
163        //  (now that we know the PDNum & PDCnt for each entry).
164        int nDE = 0;
165        for (Entity entity : entities) {
166            if (entity.canWrite()) {
167                DirEntry de = entity.getDirectoryEntry();
168                de.write(writer);
169                nDE += 2;
170            }
171        }
172
173        //  Write out the parameter data buffered above.
174        writer.write(str.toString());
175
176        //  Finally, write out the terminate section.
177        writer.print("S");      writer.print(Constants.makeSequenceNumber(nStart));
178        writer.print("G");      writer.print(Constants.makeSequenceNumber(nGlobal));
179        writer.print("D");      writer.print(Constants.makeSequenceNumber(nDE));
180        writer.print("P");      writer.print(Constants.makeSequenceNumber(nPD));
181        writer.print("                                        ");
182        writer.print("T");      writer.print(Constants.makeSequenceNumber(1));
183
184    }
185
186    /**
187     * Get the list of error strings built up after checking the entities.
188     *
189     * @return list of error strings
190     */
191    public List<String> getErrors() {
192        return errors;
193    }
194
195    /**
196     * Return the Start Section object.
197     *
198     * @return Start Section
199     */
200    public StartSection getStartSection() {
201        return startSection;
202    }
203
204    /**
205     * Return the Global Section object.
206     *
207     * @return Global Section
208     */
209    public GlobalSection getGlobalSection() {
210        return globalSection;
211    }
212
213    /**
214     * Return the entity count.
215     *
216     * @return number of entities in part
217     */
218    public int getEntityCount() {
219        return entities.size();
220    }
221
222    /**
223     * Return list of entities.
224     *
225     * @return list of all entities in part
226     */
227    public List<Entity> getEntities() {
228        return entities;
229    }
230
231    /**
232     * Return entity with specified DE number
233     *
234     * @param de DE number of entity to be returned
235     * @return entity with specified DE number
236     */
237    public Entity getEntity(int de) {
238        return entities.get((de - 1) / 2);
239    }
240
241    /**
242     * Return a specific entity's header for the Entity List window.
243     *
244     * @param i index of entity in list (not DE number)
245     * @return String to be placed in Entity List window
246     */
247    public String getEntityHeader(int i) {
248        return entities.get(i).getHeader();
249    }
250
251    /**
252     * Return a specific entity as a String. This is the Directory Entry and Parameter
253     * Data for the requested index.
254     *
255     * @param i index of entity in list (not DE number)
256     * @return string with entity's information
257     */
258    public String toString(int i) {
259        return entities.get(i).toString();
260    }
261
262    /**
263     * Return info on the whole part as a String. This includes the number of entities.
264     *
265     * @return string containing info on whole part
266     */
267    @Override
268    public String toString() {
269        String outStr = "Part:\n";
270
271        outStr = outStr + "# Entities = " + entities.size();
272
273        return outStr;
274    }
275
276    /**
277     * Sort the entities. This uses the QuickSort algorithm.
278     *
279     * @param lo0  low index of range to sort
280     * @param hi0  high index of range to sort
281     * @param flag either SORT_BY_DENUM or SORT_BY_PDNUM
282     */
283    private void sort(int lo0, int hi0, int flag) {
284        int lo = lo0;
285        int hi = hi0;
286        int mid = lo0;
287
288        if (hi0 > lo0) {
289            if (flag == SORT_BY_DENUM)
290                mid = entities.get((lo0 + hi0) / 2).getDENum();
291            else if (flag == SORT_BY_PDNUM)
292                mid = entities.get((lo0 + hi0) / 2).getPDNum();
293
294            while (lo <= hi) {
295                if (flag == SORT_BY_DENUM) {
296                    while ((lo < hi0) && entities.get(lo).getDENum() < mid)
297                        ++lo;
298
299                    while ((hi > lo0) && entities.get(hi).getDENum() > mid)
300                        --hi;
301
302                } else if (flag == SORT_BY_PDNUM) {
303                    while ((lo < hi0) && entities.get(lo).getPDNum() < mid)
304                        ++lo;
305
306                    while ((hi > lo0) && entities.get(hi).getPDNum() > mid)
307                        --hi;
308                }
309
310                if (lo <= hi) {
311                    swap(lo, hi);
312                    ++lo;
313                    --hi;
314                }
315            }
316
317            if (lo0 < hi)
318                sort(lo0, hi, flag);
319
320            if (lo < hi0)
321                sort(lo, hi0, flag);
322        }
323    }
324
325    /**
326     * Swap routine. Used in QuickSort algorithm.
327     *
328     * @param i index of 1st value to swap
329     * @param j index of 2nd value to swap
330     */
331    private void swap(int i, int j) {
332        Entity T = entities.get(i);
333        entities.set(i, entities.get(j));
334        entities.set(j, T);
335    }
336}