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}