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}